lib/byebug/runner.rb
# frozen_string_literal: true
require "optparse"
require "English"
require_relative "core"
require_relative "version"
require_relative "helpers/bin"
require_relative "helpers/parse"
require_relative "helpers/string"
require_relative "option_setter"
require_relative "processors/control_processor"
module Byebug
#
# Responsible for starting the debugger when started from the command line.
#
class Runner
include Helpers::BinHelper
include Helpers::ParseHelper
include Helpers::StringHelper
#
# Special working modes that don't actually start the debugger.
#
attr_reader :help, :version, :remote
#
# Signals that we should exit after the debugged program is finished.
#
attr_accessor :quit
#
# Signals that we should stop before program starts
#
attr_accessor :stop
#
# Signals that we should run rc scripts before program starts
#
attr_writer :init_script
#
# @param stop [Boolean] Whether the runner should stop right before
# starting the program.
#
# @param quit [Boolean] Whether the runner should quit right after
# finishing the program.
#
def initialize(stop = true, quit = true)
@stop = stop
@quit = quit
end
def help=(text)
@help ||= text
interface.puts("#{text}\n")
end
def version=(number)
@version ||= number
interface.puts prettify <<-VERSION
Running byebug #{number}
VERSION
end
def remote=(host_and_port)
@remote ||= Byebug.parse_host_and_port(host_and_port)
Byebug.start_client(*@remote)
end
def init_script
defined?(@init_script) ? @init_script : true
end
#
# Usage banner.
#
def banner
prettify <<-BANNER
byebug #{Byebug::VERSION}
Usage: byebug [options] <script.rb> -- <script.rb parameters>
BANNER
end
#
# Starts byebug to debug a program.
#
def run
Byebug.mode = :standalone
option_parser.order!($ARGV)
return if non_script_option? || error_in_script?
$PROGRAM_NAME = program
Byebug.run_init_script if init_script
loop do
debug_program
break if quit
ControlProcessor.new(nil, interface).process_commands
end
end
def interface
@interface ||= Context.interface
end
#
# Processes options passed from the command line.
#
def option_parser
@option_parser ||= OptionParser.new(banner, 25) do |opts|
opts.banner = banner
OptionSetter.new(self, opts).setup
end
end
def program
@program ||= begin
candidate = which($ARGV.shift)
if [which("ruby"), RbConfig.ruby].include?(candidate)
which($ARGV.shift)
else
candidate
end
end
end
#
# An option that doesn't need a script specified was given
#
def non_script_option?
version || help || remote
end
#
# There is an error with the specified script
#
def error_in_script?
no_script? || non_existing_script? || invalid_script?
end
#
# No script to debug specified
#
def no_script?
return false unless $ARGV.empty?
print_error("You must specify a program to debug")
true
end
#
# Extracts debugged program from command line args.
#
def non_existing_script?
return false if program
print_error("The script doesn't exist")
true
end
#
# Checks the debugged script has correct syntax
#
def invalid_script?
return false if syntax_valid?(File.read(program))
print_error("The script has incorrect syntax")
true
end
#
# Debugs a script only if syntax checks okay.
#
def debug_program
error = Byebug.debug_load(program, stop)
puts "#{error}\n#{error.backtrace}" if error
end
#
# Prints an error message and a help string
#
def print_error(msg)
interface.errmsg(msg)
interface.puts(option_parser.help)
end
end
end