lib/pry/byebug/breakpoints.rb
# frozen_string_literal: true
class Pry
module Byebug
#
# Wrapper for Byebug.breakpoints that respects our Processor and has better
# failure behavior. Acts as an Enumerable.
#
module Breakpoints
extend Enumerable
extend self
#
# Breakpoint in a file:line location
#
class FileBreakpoint < SimpleDelegator
def source_code
Pry::Code.from_file(source).around(pos, 3).with_marker(pos)
end
def to_s
"#{source} @ #{pos}"
end
end
#
# Breakpoint in a Class#method location
#
class MethodBreakpoint < SimpleDelegator
def initialize(byebug_bp, method)
__setobj__ byebug_bp
@method = method
end
def source_code
Pry::Code.from_method(Pry::Method.from_str(@method))
end
def to_s
@method
end
end
def breakpoints
@breakpoints ||= []
end
#
# Adds a method breakpoint.
#
def add_method(method, expression = nil)
validate_expression expression
owner, name = method.split(/[\.#]/)
byebug_bp = ::Byebug::Breakpoint.add(owner, name.to_sym, expression)
bp = MethodBreakpoint.new byebug_bp, method
breakpoints << bp
bp
end
#
# Adds a file breakpoint.
#
def add_file(file, line, expression = nil)
real_file = (file != Pry.eval_path)
raise(ArgumentError, "Invalid file!") if real_file && !File.exist?(file)
validate_expression expression
path = (real_file ? File.expand_path(file) : file)
bp = FileBreakpoint.new ::Byebug::Breakpoint.add(path, line, expression)
breakpoints << bp
bp
end
#
# Changes the conditional expression for a breakpoint.
#
def change(id, expression = nil)
validate_expression expression
breakpoint = find_by_id(id)
breakpoint.expr = expression
breakpoint
end
#
# Deletes an existing breakpoint with the given ID.
#
def delete(id)
deleted =
::Byebug::Breakpoint.remove(id) &&
breakpoints.delete(find_by_id(id))
raise(ArgumentError, "No breakpoint ##{id}") unless deleted
end
#
# Deletes all breakpoints.
#
def delete_all
@breakpoints = []
::Byebug.breakpoints.clear
end
#
# Enables a disabled breakpoint with the given ID.
#
def enable(id)
change_status id, true
end
#
# Disables a breakpoint with the given ID.
#
def disable(id)
change_status id, false
end
#
# Disables all breakpoints.
#
def disable_all
each do |breakpoint|
breakpoint.enabled = false
end
end
def to_a
breakpoints
end
def size
to_a.size
end
def each(&block)
to_a.each(&block)
end
def last
to_a.last
end
def find_by_id(id)
breakpoint = find { |b| b.id == id }
raise(ArgumentError, "No breakpoint ##{id}!") unless breakpoint
breakpoint
end
private
def change_status(id, enabled = true)
breakpoint = find_by_id(id)
breakpoint.enabled = enabled
breakpoint
end
def validate_expression(exp)
valid = exp && (exp.empty? || !Pry::Code.complete_expression?(exp))
return unless valid
raise("Invalid breakpoint conditional: #{expression}")
end
end
end
end