1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
# frozen_string_literal: true
require "byebug/core"
module Byebug
#
# Extends raw byebug's processor.
#
class PryProcessor < CommandProcessor
attr_accessor :pry
extend Forwardable
def_delegators :@pry, :output
def_delegators Pry::Helpers::Text, :bold
def self.start
Byebug.start
Setting[:autolist] = false
Context.processor = self
Byebug.current_context.step_out(4, true)
end
#
# Wrap a Pry REPL to catch navigational commands and act on them.
#
def run(&_block)
return_value = nil
command = catch(:breakout_nav) do # Throws from PryByebug::Commands
return_value = allowing_other_threads { yield }
{} # Nothing thrown == no navigational command
end
# Pry instance to resume after stepping
@pry = command[:pry]
perform(command[:action], command[:options])
return_value
end
#
# Set up a number of navigational commands to be performed by Byebug.
#
def perform(action, options = {})
return unless %i[
backtrace
down
finish
frame
next
step
up
].include?(action)
send("perform_#{action}", options)
end
# --- Callbacks from byebug C extension ---
#
# Called when the debugger wants to stop at a regular line
#
def at_line
resume_pry
end
#
# Called when the debugger wants to stop right before a method return
#
def at_return(_return_value)
resume_pry
end
#
# Called when the debugger wants to stop right before the end of a class
# definition
#
def at_end
resume_pry
end
#
# Called when a breakpoint is hit. Note that `at_line`` is called
# inmediately after with the context's `stop_reason == :breakpoint`, so we
# must not resume the pry instance here
#
def at_breakpoint(breakpoint)
@pry ||= Pry.new
output.puts bold("\n Breakpoint #{breakpoint.id}. ") + n_hits(breakpoint)
expr = breakpoint.expr
return unless expr
output.puts bold("Condition: ") + expr
end
private
def n_hits(breakpoint)
n_hits = breakpoint.hit_count
n_hits == 1 ? "First hit" : "Hit #{n_hits} times."
end
#
# Resume an existing Pry REPL at the paused point.
#
def resume_pry
new_binding = frame._binding
run do
if defined?(@pry) && @pry
@pry.repl(new_binding)
else
@pry = Pry.start_without_pry_byebug(new_binding)
end
end
end
def perform_backtrace(_options)
Byebug::WhereCommand.new(self, "backtrace").execute
resume_pry
end
def perform_next(options)
lines = (options[:lines] || 1).to_i
context.step_over(lines, frame.pos)
end
def perform_step(options)
times = (options[:times] || 1).to_i
context.step_into(times, frame.pos)
end
def perform_finish(*)
context.step_out(1)
end
def perform_up(options)
times = (options[:times] || 1).to_i
Byebug::UpCommand.new(self, "up #{times}").execute
resume_pry
end
def perform_down(options)
times = (options[:times] || 1).to_i
Byebug::DownCommand.new(self, "down #{times}").execute
resume_pry
end
def perform_frame(options)
index = options[:index] ? options[:index].to_i : ""
Byebug::FrameCommand.new(self, "frame #{index}").execute
resume_pry
end
end
end
|