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
@@ -21,6 +21,11 @@ module PryMoves
21
21
  breakout_navigation :next, 'blockless'
22
22
  end
23
23
 
24
+ block_command 'next_breakpoint', 'Go to next breakpoint' do |param|
25
+ breakout_navigation :next_breakpoint, param
26
+ end
27
+ alias_command 'b', 'next_breakpoint'
28
+
24
29
  block_command 'iterate', 'Go to next iteration of current block' do |param|
25
30
  breakout_navigation :iterate, param
26
31
  end
@@ -41,7 +46,7 @@ module PryMoves
41
46
  end
42
47
 
43
48
  block_command 'bt', 'Backtrace' do |param, param2|
44
- PryMoves::Backtrace.new(target, _pry_).run_command param, param2
49
+ PryMoves::Backtrace.new(_pry_).run_command param, param2
45
50
  end
46
51
 
47
52
  block_command 'debug', '' do
@@ -49,6 +54,23 @@ module PryMoves
49
54
  breakout_navigation :debug, cmd
50
55
  end
51
56
 
57
+ block_command :restart, '' do
58
+ PryMoves.restart_requested = true
59
+ run 'continue'
60
+ end
61
+ alias_command '@', 'restart'
62
+
63
+ block_command :reload, '' do
64
+ PryMoves.reload_requested = true
65
+ run 'continue'
66
+ end
67
+ alias_command '#', 'reload'
68
+
69
+ block_command /^\\(\w+)$/, 'Execute command explicitly' do |param|
70
+ Pry.config.ignore_once_var_precedence = true
71
+ run param
72
+ end
73
+
52
74
  block_command '!', 'exit' do
53
75
  PryMoves.unlock
54
76
  Pry.config.exit_requested = true
@@ -62,16 +84,32 @@ module PryMoves
62
84
 
63
85
  helpers do
64
86
  def breakout_navigation(action, param)
87
+ return if var_precedence action
88
+
65
89
  check_file_context
66
90
  _pry_.binding_stack.clear # Clear the binding stack.
67
91
  throw :breakout_nav, { # Break out of the REPL loop and
68
92
  action: action, # signal the tracer.
69
93
  param: param,
70
- binding: target,
71
- pry: _pry_
94
+ binding: target
72
95
  }
73
96
  end
74
97
 
98
+ def var_precedence action
99
+ if Pry.config.ignore_once_var_precedence
100
+ Pry.config.ignore_once_var_precedence = false
101
+ return
102
+ end
103
+
104
+ input = Pry.config.original_user_input || action
105
+ binding_value = target.eval(input) rescue nil
106
+ unless binding_value.nil?
107
+ puts "ℹ️️ Variable \"#{input}\" found. To execute command type its alias or \\#{input}"
108
+ puts PryMoves::Painter.colorize binding_value
109
+ true
110
+ end
111
+ end
112
+
75
113
  # Ensures that a command is executed in a local file context.
76
114
  def check_file_context
77
115
  unless PryMoves.check_file_context(target)
@@ -79,6 +117,7 @@ module PryMoves
79
117
  end
80
118
  end
81
119
  end
120
+
82
121
  end
83
122
  end
84
123
 
@@ -0,0 +1,74 @@
1
+ class PryMoves::Formatter
2
+
3
+ attr_accessor :colorize
4
+
5
+ def initialize colorize = true
6
+ @colorize = colorize
7
+ end
8
+
9
+ def method_signature(binding)
10
+ meth = binding.eval('__method__')
11
+ meth_obj = meth ? Pry::Method.from_binding(binding) : nil
12
+ if !meth_obj
13
+ ""
14
+ elsif meth_obj.undefined?
15
+ "#{meth_obj.name}(UNKNOWN) (undefined method)"
16
+ else
17
+ args = meth_obj.parameters.map.with_index do |(type, name), i|
18
+ if name
19
+ value = format_arg binding, name.to_s
20
+ show_value = true
21
+ else
22
+ name = (type == :block ? 'block' : "arg#{i + 1}")
23
+ end
24
+ name = case type
25
+ when :req then "#{name} ="
26
+ when :key then "#{name}:"
27
+ when :opt then "#{name}=?"
28
+ when :rest then "*#{name}"
29
+ when :block then "&#{name}"
30
+ else '?'
31
+ end
32
+ show_value ? "#{name} #{value}" : name
33
+ end
34
+ "#{meth_obj.name}(#{args.join(', ')})"
35
+ end
36
+ end
37
+
38
+ def format_arg binding, arg_name
39
+ arg = binding.eval(arg_name.to_s)
40
+ format_obj arg
41
+ end
42
+
43
+ def first_line str
44
+ str.split("\n").first
45
+ end
46
+
47
+ def cut_string str
48
+ str.length > 50 ? "#{str.first 50}..." : str
49
+ end
50
+
51
+ PATH_TRASH = defined?(Rails) ? Rails.root.to_s : Dir.pwd
52
+
53
+ def shorten_path(path)
54
+ path.gsub( /^#{PATH_TRASH}\//, '')
55
+ end
56
+
57
+ def format_obj(obj)
58
+ if obj.is_a? String
59
+ format_obj2 cut_string first_line obj
60
+ else
61
+ first_line format_obj2 obj
62
+ end
63
+ end
64
+
65
+ def format_obj2(obj)
66
+ if @colorize
67
+ PryMoves::Painter.colorize obj
68
+ else
69
+ i = obj.inspect
70
+ i.start_with?('#<') ? obj.class.to_s : i
71
+ end
72
+ end
73
+
74
+ end
@@ -14,13 +14,14 @@ module PryMoves::Painter
14
14
 
15
15
  def self.colorize(obj)
16
16
  colored_str = Canvas.new
17
- obj = obj.class if obj.inspect.start_with? "#<"
17
+ i = obj.inspect
18
+ obj = obj.class if i.is_a?(String) && i.start_with?("#<")
18
19
  catch (:cut) do
19
20
  Pry::ColorPrinter.pp obj, colored_str
20
21
  end
21
22
  colored_str.chomp
22
23
  rescue => e
23
- "Inspect error: #{e}\n" +
24
+ "⛔️ Inspect error: #{e}\n" +
24
25
  "#{e.backtrace.first(3).join("\n")}"
25
26
  end
26
27
 
@@ -1,31 +1,31 @@
1
1
  class << Pry
2
- alias_method :start_without_pry_nav, :start
3
-
4
- def start_with_pry_nav(target = TOPLEVEL_BINDING, options = {})
5
- old_options = options.reject { |k, _| k == :pry_remote }
2
+ alias pry_moves_origin_start start
6
3
 
4
+ def start(target = TOPLEVEL_BINDING, options = {})
7
5
  if target.is_a?(Binding) && PryMoves.check_file_context(target)
8
6
  # Wrap the tracer around the usual Pry.start
9
- PryMoves::PryWrapper.new(target, options).run do
10
- start_without_pry_nav(target, old_options)
11
- end
7
+ original_verbosity = $VERBOSE
8
+ $VERBOSE = nil # Disable warnings for pry-moves
9
+ PryMoves::PryWrapper.new(target, options, self).run
10
+ $VERBOSE = original_verbosity
12
11
  else
13
12
  # No need for the tracer unless we have a file context to step through
14
- start_without_pry_nav(target, old_options)
13
+ pry_moves_origin_start(target, options)
15
14
  end
16
15
  end
17
16
 
18
- alias_method :start, :start_with_pry_nav
19
17
  end
20
18
 
21
19
  Binding.class_eval do
22
20
 
21
+ attr_accessor :index
22
+
23
23
  alias pry_forced pry
24
24
 
25
25
  def pry
26
- unless Pry.config.disable_breakpoints
27
- PryMoves.synchronize_threads ||
28
- return # Don't start binding.pry when semaphore locked by current thread
26
+ if !Pry.config.disable_breakpoints and
27
+ # Don't start binding.pry when semaphore locked by current thread
28
+ PryMoves.synchronize_threads
29
29
  pry_forced
30
30
  end
31
31
  end
@@ -34,6 +34,32 @@ end
34
34
 
35
35
  Pry.config.pager = false
36
36
 
37
+ Pry::Command.class_eval do
38
+ class << self
39
+ attr_accessor :original_user_input
40
+ end
41
+
42
+ alias run_origin_for_pry_moves run
43
+ def run(command_string, *args)
44
+ Pry.config.original_user_input = self.class.original_user_input
45
+ result = run_origin_for_pry_moves command_string, *args
46
+ Pry.config.original_user_input = nil
47
+ result
48
+ end
49
+ end
50
+
51
+ Pry::CommandSet.class_eval do
52
+
53
+ alias alias_command_origin_for_pry_moves alias_command
54
+
55
+ def alias_command(match, action, options = {})
56
+ cmd = alias_command_origin_for_pry_moves match, action, options
57
+ cmd.original_user_input = match
58
+ cmd
59
+ end
60
+
61
+ end
62
+
37
63
  Pry::Command::Whereami.class_eval do
38
64
  # Negligent function from Pry - evidently poor output format
39
65
  # would be wanted to be changed often by developers,
@@ -56,11 +82,19 @@ Pry::Command::Whereami.class_eval do
56
82
  end
57
83
 
58
84
  def build_output
59
- lines = []
60
- lines << "#{text.bold('From:')} #{PryMoves::Helpers.shorten_path location}"
61
- lines << PryMoves::Watch.instance.output(target) unless PryMoves::Watch.instance.empty?
85
+ lines = ['']
86
+
87
+ formatter = PryMoves::Formatter.new
88
+ prefix = Thread.current[:pry_moves_debug] ? "👾 " : ""
89
+ lines << "#{prefix}#{formatter.shorten_path location}"
90
+ lines << " ." + formatter.method_signature(target)
62
91
  lines << ''
63
92
  lines << "#{code.with_line_numbers(use_line_numbers?).with_marker(marker).highlighted}"
93
+
94
+ lines << PryMoves::Watch.instance.output(target) unless PryMoves::Watch.instance.empty?
95
+ lines.concat PryMoves.messages
96
+ PryMoves.messages.clear
97
+
64
98
  lines << ''
65
99
  lines.join "\n"
66
100
  end
@@ -82,4 +116,18 @@ Pry::Code::LOC.class_eval do
82
116
  tuple[0] = " #{marker} #{ line }"
83
117
  end
84
118
 
85
- end
119
+ end
120
+
121
+ Pry::Output.class_eval do
122
+
123
+ alias pry_moves_origin_for_puts puts
124
+
125
+ def puts *args
126
+ first = args[0]
127
+ if first.is_a? String and first.start_with? "(pry) output error"
128
+ first.slice! 400..-1
129
+ end
130
+ pry_moves_origin_for_puts *args
131
+ end
132
+
133
+ end if defined? Pry::Output
@@ -2,24 +2,24 @@ require 'pry' unless defined? Pry
2
2
 
3
3
  module PryMoves
4
4
  class PryWrapper
5
- def initialize(binding_, pry_start_options = {})
5
+ def initialize(binding_, pry_start_options, pry)
6
6
  @init_binding = binding_
7
7
  @pry_start_options = pry_start_options # Options to use for Pry.start
8
+ @pry = pry
8
9
  end
9
10
 
10
- def run(&block)
11
+ def run
11
12
  PryMoves.lock
12
13
 
13
- Pry.config.marker = "⛔️" if @pry_start_options[:exit_from_method]
14
-
15
- return_value = nil
16
- PryMoves.is_open = true
17
- @command = catch(:breakout_nav) do # Coordinates with PryMoves::Commands
18
- return_value = yield
19
- nil # Nothing thrown == no navigational command
14
+ initial_frame = PryMoves::BindingsStack.new.initial_frame
15
+ if not @pry_start_options[:pry_moves_loop] and
16
+ initial_frame.local_variable_defined? :debug_redirect
17
+ debug_redirect = initial_frame.local_variable_get(:debug_redirect)
18
+ PryMoves.messages << "⏩ redirected to #{debug_redirect}"
19
+ @command = {action: :step, binding: initial_frame}
20
+ else
21
+ start_pry
20
22
  end
21
- PryMoves.is_open = false
22
- Pry.config.marker = "=>"
23
23
 
24
24
  if @command
25
25
  trace_command
@@ -30,11 +30,24 @@ class PryWrapper
30
30
  end
31
31
  end
32
32
 
33
- return_value
33
+ @return_value
34
34
  end
35
35
 
36
36
  private
37
37
 
38
+ def start_pry
39
+ Pry.config.marker = "⛔️" if @pry_start_options[:exit_from_method]
40
+ PryMoves.is_open = true
41
+
42
+ @command = catch(:breakout_nav) do # Coordinates with PryMoves::Commands
43
+ @return_value = @pry.pry_moves_origin_start(@init_binding, @pry_start_options)
44
+ nil # Nothing thrown == no navigational command
45
+ end
46
+
47
+ PryMoves.is_open = false
48
+ Pry.config.marker = "=>"
49
+ end
50
+
38
51
  def trace_command
39
52
  if @command[:action] == :debug
40
53
  wrap_debug
@@ -61,9 +74,9 @@ class PryWrapper
61
74
  tracer = start_tracing
62
75
  begin
63
76
  @command[:binding].eval @command[:param]
64
- rescue => e
77
+ rescue StandardError, SyntaxError => e
65
78
  Thread.current.set_trace_func nil
66
- puts e
79
+ puts "❌️ #{e}"
67
80
  end
68
81
  tracer.stop_tracing
69
82
  end.join
@@ -73,9 +86,9 @@ class PryWrapper
73
86
 
74
87
  def start_tracing
75
88
  @last_runtime_binding = @command[:binding]
76
- tracer = PryMoves::Tracer.new @command, @pry_start_options
77
- tracer.trace
78
- tracer
89
+ PryMoves::TraceCommand.trace @command, @pry_start_options do |binding|
90
+ Pry.start(binding, @pry_start_options)
91
+ end
79
92
  end
80
93
 
81
94
  end
@@ -0,0 +1,38 @@
1
+ module PryMoves::Restartable
2
+
3
+ attr_accessor :restart_requested, :reload_requested,
4
+ :reload_rake_tasks
5
+
6
+ def restartable
7
+ trigger :new_run
8
+ yield
9
+ re_execution
10
+ rescue PryMoves::Restart
11
+ self.restart_requested = false
12
+ PryMoves.reset
13
+ trigger :restart
14
+ retry
15
+ rescue PryMoves::Reload
16
+ puts "🔮 try to use @ with reload"
17
+ exit 3
18
+ end
19
+
20
+ def re_execution
21
+ raise PryMoves::Restart if restart_requested
22
+ raise PryMoves::Reload if reload_requested
23
+ end
24
+
25
+
26
+ end
27
+
28
+ class PryMoves::Restart < RuntimeError
29
+ end
30
+ class PryMoves::Reload < RuntimeError
31
+ end
32
+ RSpec::Support::AllExceptionsExceptOnesWeMustNotRescue::AVOID_RESCUING.concat [PryMoves::Restart, PryMoves::Reload] if defined? RSpec
33
+
34
+ Pry.config.hooks.add_hook(:after_eval, :exit_on_re_execution) do |_, _, _pry_|
35
+ if PryMoves.restart_requested or PryMoves.reload_requested
36
+ Pry.run_command 'exit-all'
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module PryMoves
2
- VERSION = '0.1.13'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'singleton'
2
+ require 'set'
2
3
 
3
4
  class PryMoves::Watch
4
5
 
@@ -44,6 +45,8 @@ class PryMoves::Watch
44
45
  "\033[1m#{cmd}\033[0m: #{format binding_.eval(cmd)}"
45
46
  rescue NameError
46
47
  "\033[1m#{cmd}\033[0m: <undefined>"
48
+ rescue => e
49
+ "\033[1m#{cmd}\033[0m: <#{e}>"
47
50
  end
48
51
 
49
52
  def format(text)
data/lib/pry-moves.rb CHANGED
@@ -1,19 +1,30 @@
1
1
  require 'pry' unless defined? Pry
2
2
 
3
3
  require 'pry-moves/version'
4
- require 'pry-moves/trace_commands'
5
- require 'pry-moves/traced_method'
6
- require 'pry-moves/tracer'
7
4
  require 'pry-moves/pry_ext'
8
5
  require 'pry-moves/commands'
9
- require 'pry-moves/traversing'
6
+ require 'pry-moves/add_suffix'
10
7
  require 'pry-moves/pry_wrapper'
8
+ require 'pry-moves/bindings_stack'
9
+ require 'pry-moves/formatter'
11
10
  require 'pry-moves/backtrace'
12
11
  require 'pry-moves/watch'
13
- require 'pry-moves/helpers'
14
12
  require 'pry-moves/painter'
13
+ require 'pry-moves/restartable'
14
+
15
+ require 'commands/traced_method'
16
+ require 'commands/trace_helpers'
17
+ require 'commands/trace_command'
18
+ require 'commands/debug'
19
+ require 'commands/finish'
20
+ require 'commands/goto'
21
+ require 'commands/iterate'
22
+ require 'commands/next'
23
+ require 'commands/next_breakpoint'
24
+ require 'commands/step'
15
25
 
16
26
  require 'pry-stack_explorer/pry-stack_explorer'
27
+ require 'debug_sugar'
17
28
 
18
29
  # Optionally load pry-remote monkey patches
19
30
  require 'pry-moves/pry_remote_ext' if defined? PryRemote
@@ -22,8 +33,30 @@ module PryMoves
22
33
  TRACE_IGNORE_FILES = Dir[File.join(File.dirname(__FILE__), '**', '*.rb')].map { |f| File.expand_path(f) }
23
34
 
24
35
  extend self
36
+ extend PryMoves::Restartable
37
+
38
+ attr_accessor :is_open, :trace,
39
+ :stop_on_breakpoints, :launched_specs_examples, :debug_called_times
40
+
41
+ def reset
42
+ self.launched_specs_examples = 0
43
+ self.stop_on_breakpoints = true
44
+ self.debug_called_times = 0
45
+ end
25
46
 
26
- attr_accessor :is_open
47
+ def debug(message = nil, at: nil)
48
+ pry_moves_stack_root = true
49
+ PryMoves.re_execution
50
+ if PryMoves.stop_on_breakpoints
51
+ if at
52
+ self.debug_called_times += 1
53
+ return unless self.debug_called_times == at
54
+ end
55
+ PryMoves.messages << message if message
56
+ binding.pry
57
+ PryMoves.re_execution
58
+ end
59
+ end
27
60
 
28
61
  # Checks that a binding is in a local file context. Extracted from
29
62
  # https://github.com/pry/pry/blob/master/lib/pry/default_commands/context.rb
@@ -36,9 +69,18 @@ module PryMoves
36
69
  @semaphore ||= Mutex.new
37
70
  end
38
71
 
72
+ def messages
73
+ @messages ||= []
74
+ end
75
+
76
+ def add_command(command, &block)
77
+ Pry.commands.block_command command, "", &block
78
+ end
79
+
39
80
  def locked?
40
81
  semaphore.locked?
41
82
  end
83
+ alias tracing? locked?
42
84
 
43
85
  def lock
44
86
  semaphore.lock unless semaphore.locked?
@@ -59,6 +101,23 @@ module PryMoves
59
101
  true
60
102
  end
61
103
 
104
+ def trigger(event)
105
+ triggers[event].each &:call
106
+ end
107
+
108
+ def triggers
109
+ @triggers ||= Hash.new do |hash, key|
110
+ hash[key] = []
111
+ end
112
+ end
113
+
114
+ def on(trigger, &block)
115
+ triggers[trigger] << block
116
+ end
117
+
62
118
  # Reference to currently running pry-remote server. Used by the tracer.
63
119
  attr_accessor :current_remote_server
64
120
  end
121
+
122
+ PryMoves.reset
123
+ PryMoves.trace = true if ENV['TRACE_MOVES']