pry-moves 0.1.13 → 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.
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']