lib/byebug.rb
require 'byebug/byebug' require 'byebug/version' require 'byebug/context' require 'byebug/interface' require 'byebug/processor' require 'byebug/setting' require 'byebug/remote' require 'stringio' require 'tracer' require 'linecache19' module Byebug # List of files byebug will ignore while debugging IGNORED_FILES = Dir.glob(File.expand_path('../**/*.rb', __FILE__)) # Configuration file used for startup commands. Default value is .byebugrc INITFILE = '.byebugrc' unless defined?(INITFILE) # Stores program being debugged to make restarts possible PROG_SCRIPT = $0 unless defined?(PROG_SCRIPT) class << self # processor modules provide +handler+ object attr_accessor :handler Byebug.handler = CommandProcessor.new def source_reload Object.send(:remove_const, 'SCRIPT_LINES__') if Object.const_defined?('SCRIPT_LINES__') Object.const_set('SCRIPT_LINES__', {}) end # # Get line +line_number+ from file named +filename+. # # @return "\n" if there was a problem. Leaking blanks are stripped off. # def line_at(filename, line_number) source_reload return "\n" unless File.exist?(filename) line = Tracer::Single.get_line(filename, line_number) return "#{line.gsub(/^\s+/, '').chomp}" end # # Add a new breakpoint # # @param [String] file # @param [Fixnum] line # @param [String] expr # def add_breakpoint(file, line, expr=nil) breakpoint = Breakpoint.new(file, line, expr) breakpoints << breakpoint breakpoint end # # Remove a breakpoint # # @param [integer] breakpoint number # def remove_breakpoint(id) breakpoints.reject! { |b| b.id == id } end def interface=(value) handler.interface = value end extend Forwardable def_delegators :"handler.interface", :print # # Runs normal byebug initialization scripts. # # Reads and executes the commands from init file (if any) in the current # working directory. This is only done if the current directory is # different from your home directory. Thus, you can have more than one init # file, one generic in your home directory, and another, specific to the # program you are debugging, in the directory where you invoke byebug. # def run_init_script(out = handler.interface) cwd_script = File.expand_path(File.join(".", INITFILE)) run_script(cwd_script, out) if File.exist?(cwd_script) home_script = File.expand_path(File.join(ENV['HOME'].to_s, INITFILE)) if File.exist?(home_script) and cwd_script != home_script run_script(home_script, out) end end # # Runs a script file # def run_script(file, out = handler.interface, verbose=false) interface = ScriptInterface.new(File.expand_path(file), out) processor = ControlCommandProcessor.new(interface) processor.process_commands(verbose) end # # Activates the post-mortem mode. # # By calling Byebug.post_mortem method, you install an at_exit hook that # intercepts any exception not handled by your script and enables # post-mortem mode. # def post_mortem return if self.post_mortem? self.post_mortem = true at_exit { handle_post_mortem if post_mortem? } end def handle_post_mortem context = raised_exception.__bb_context file = raised_exception.__bb_file line = raised_exception.__bb_line handler.at_line(context, file, line) end private :handle_post_mortem end end class Exception attr_reader :__bb_file, :__bb_line, :__bb_binding, :__bb_context end module Kernel # # Enters byebug right before (or right after if _before_ is false) return # events occur. Before entering byebug the init script is read. # def byebug(steps_out = 1, before = true) Byebug.run_init_script(StringIO.new) Byebug.start Byebug.current_context.step_out(steps_out, before) end alias_method :debugger, :byebug end