require 'byebug/states/regular_state' require 'byebug/helpers/file' module Byebug # # Processes commands in regular mode # class CommandProcessor < Processor include Helpers::FileHelper attr_reader :display, :state def initialize(interface = LocalInterface.new) super(interface) @display = [] @last_cmd = nil # To allow empty (just ) commands @context_was_dead = false # Assume we haven't started. end def interface=(interface) @interface.close if @interface @interface = interface end def at_breakpoint(_context, breakpoint) n = Byebug.breakpoints.index(breakpoint) + 1 file = normalize(breakpoint.source) line = breakpoint.pos puts "Stopped by breakpoint #{n} at #{file}:#{line}" end def at_catchpoint(context, excpt) file = normalize(context.frame_file(0)) line = context.frame_line(0) puts "Catchpoint at #{file}:#{line}: `#{excpt}' (#{excpt.class})" end def at_tracing(context, file, line) puts "Tracing: #{normalize(file)}:#{line} #{get_line(file, line)}" always_run(context, file, line, 2) end def at_line(context, file, line) process_commands(context, file, line) end def at_return(context, file, line) process_commands(context, file, line) end private # # Prompt shown before reading a command. # def prompt(context) "(byebug#{context.dead? ? ':post-mortem' : ''}) " end # # Run commands everytime. # # For example display commands or possibly the list or irb in an "autolist" # or "autoirb". # # @return List of commands acceptable to run bound to the current state # def always_run(context, file, line, run_level) @state = RegularState.new(context, @display, file, @interface, line) # Change default when in irb or code included in command line Setting[:autolist] = false if ['(irb)', '-e'].include?(file) # Bind commands to the current state. commands.each { |cmd| cmd.execute if cmd.class.always_run >= run_level } end def commands Byebug.commands.map { |cmd| cmd.new(state) } end # # Handle byebug commands. # def process_commands(context, file, line) always_run(context, file, line, 1) puts 'The program finished.' if program_just_finished?(context) puts(state.location) if Setting[:autolist] == 0 @interface.autorestore repl(context) ensure @interface.autosave end # # Main byebug's REPL # def repl(context) until state.proceed? cmd = @interface.read_command(prompt(context)) return unless cmd next if cmd == '' && @last_cmd.nil? cmd.empty? ? cmd = @last_cmd : @last_cmd = cmd one_cmd(context, cmd) end end # # Autoevals a single command # def one_unknown_cmd(input) unless Setting[:autoeval] return errmsg("Unknown command: \"#{input}\". Try \"help\"") end eval_cmd = EvalCommand.new(state) eval_cmd.match(input) eval_cmd.execute end # # # Executes a single byebug command # def one_cmd(context, input) cmd = match_cmd(input) return one_unknown_cmd(input) unless cmd if context.dead? && !cmd.class.allow_in_post_mortem return errmsg('Command unavailable in post mortem mode.') end cmd.execute end # # Finds a matches the command matching the input # def match_cmd(input) commands.find { |cmd| cmd.match(input) } end # # Returns true first time control is given to the user after program # termination. # def program_just_finished?(context) result = context.dead? && !@context_was_dead @context_was_dead = false if result == true result end end end