class Byebug::InfoCommand

Implements byebug “info” command.

def description

def description
  %{
    info[ subcommand]
    Generic command for showing things about the program being
  }
end

def execute

def execute
  return help(@match) unless @match[1]
  args = @match[1].split(/[ \t]+/)
  param = args.shift
  subcmd = find(Subcommands, param)
  if subcmd
    send("info_#{subcmd.name}", *args)
  else
    errmsg "Unknown info command #{param}\n"
  end
end

def help(args)

def help(args)
  if args[1]
    subcmd = find(Subcommands, args[1])
    if subcmd
      str = subcmd.short_help + '.'
      if 'file' == subcmd.name and args[2]
        subsubcmd = find(InfoFileSubcommands, args[2])
        if subsubcmd
          str += "\nInvalid \"file\" attribute \"#{args[2]}\"."
        else
          str += "\n" + subsubcmd.short_help + '.'
        end
      else
        str += "\n" + subcmd.long_help if subcmd.long_help
      end
    else
      str = "Invalid \"info\" subcommand \"#{args[1]}\"."
    end
  else
    str = InfoCommand.description + format_subcmds(Subcommands)
  end
  print str
end

def info_args(*args)

def info_args(*args)
  unless @state.context
    print "No frame selected.\n"
    return
  end
  locals = @state.context.frame_locals(@state.frame_pos)
  args = @state.context.frame_args(@state.frame_pos)
  args.each do |name|
    s = "#{name} = #{locals[name].inspect}"
    pad_with_dots(s)
    print "#{s}\n"
  end
end

def info_breakpoints(*args)

def info_breakpoints(*args)
  return print "\"info breakpoints\" not available here.\n" unless
    @state.context
  return print "No breakpoints.\n" if Byebug.breakpoints.empty?
  brkpts = Byebug.breakpoints.sort_by{|b| b.id}
  unless args.empty?
    indices = args.map{|a| a.to_i}
    brkpts = brkpts.select{|b| indices.member?(b.id)}
    return errmsg "No breakpoints found among list given.\n" if
      brkpts.empty?
  end
  print "Num Enb What\n"
  brkpts.each do |b|
    print "%-3d %-3s at %s:%s%s\n", b.id,
                                    b.enabled? ? 'y' : 'n',
                                    b.source,
                                    b.pos,
                                    b.expr.nil? ? '' : " if #{b.expr}"
    hits = b.hit_count
    if hits > 0
      s = (hits > 1) ? 's' : ''
      print "\tbreakpoint already hit #{hits} time#{s}\n"
    end
  end
end

def info_display(*args)

def info_display(*args)
  unless @state.context
    print "info display not available here.\n"
    return
  end
  if @state.display.find{|d| d[0]}
    print "Auto-display expressions now in effect:\n"
    print "Num Enb Expression\n"
    n = 1
    for d in @state.display
      print "%3d: %s  %s\n", n, (d[0] ? 'y' : 'n'), d[1] if
        d[0] != nil
      n += 1
    end
  else
    print "There are no auto-display expressions now.\n"
  end
end

def info_file(*args)

def info_file(*args)
  return info_files unless args[0]
  file = args[0]
  param =  args[1] ? args[1] : 'basic'
  subcmd = find(InfoFileSubcommands, param)
  return errmsg "Invalid parameter #{param}\n" unless subcmd
  unless LineCache::cached?(file)
    unless LineCache::cached_script?(file)
      return print "File #{file} is not cached\n"
    end
    LineCache::cache(file, Command.settings[:autoreload])
  end
  print "File #{file}"
  info_file_path(file) if %w(all basic path).member?(subcmd.name)
  print "\n"
  info_file_lines(file) if %w(all basic lines).member?(subcmd.name)
  info_file_breakpoints(file) if %w(all breakpoints).member?(subcmd.name)
  info_file_mtime(file) if %w(all mtime).member?(subcmd.name)
  info_file_sha1(file) if %w(all sha1).member?(subcmd.name)
end

def info_file_breakpoints(file)

def info_file_breakpoints(file)
  breakpoints = LineCache.trace_line_numbers(file)
  if breakpoints
    print "\tbreakpoint line numbers:\n"
    print columnize(breakpoints.to_a.sort, Command.settings[:width])
  end
end

def info_file_lines(file)

def info_file_lines(file)
  lines = LineCache.size(file)
  print "\t %d lines\n", lines if lines
end

def info_file_mtime(file)

def info_file_mtime(file)
  stat = LineCache.stat(file)
  print "\t%s\n", stat.mtime if stat
end

def info_file_path(file)

def info_file_path(file)
  path = LineCache.path(file)
  if path != file
    print " - #{path}"
  end
end

def info_file_sha1(file)

def info_file_sha1(file)
  print "\t%s\n", LineCache.sha1(file)
end

def info_files(*args)

def info_files(*args)
  files = LineCache::cached_files
  files += SCRIPT_LINES__.keys unless 'stat' == args[0]
  files.uniq.sort.each do |file|
    stat = LineCache::stat(file)
    path = LineCache::path(file)
    print "File %s", file
    if path and path != file
      print " - %s\n", path
    else
      print "\n"
    end
    print "\t%s\n", stat.mtime if stat
  end
end

def info_global_variables(*args)

def info_global_variables(*args)
  unless @state.context
    errmsg "info global_variables not available here.\n"
    return
  end
  var_global
end

def info_instance_variables(*args)

def info_instance_variables(*args)
  unless @state.context
    print "info instance_variables not available here.\n"
    return
  end
  obj = debug_eval('self')
  var_list(obj.instance_variables)
end

def info_line(*args)

def info_line(*args)
  unless @state.context
    errmsg "info line not available here.\n"
    return
  end
  print "Line %d of \"%s\"\n",  @state.line, @state.file
end

def info_locals(*args)

def info_locals(*args)
  unless @state.context
    errmsg "info line not available here.\n"
    return
  end
  locals = @state.context.frame_locals(@state.frame_pos)
  locals.keys.sort.each do |name|
    ### FIXME: make a common routine
    begin
      s = "#{name} = #{locals[name].inspect}"
    rescue
      begin
      s = "#{name} = #{locals[name].to_s}"
      rescue
        s = "*Error in evaluation*"
      end
    end
    pad_with_dots(s)
    print "#{s}\n"
  end
end

def info_program(*args)

def info_program(*args)
  return print "The program being debugged is not being run.\n" if
    not @state.context
  return print "The program crashed.\n" + Byebug.last_exception ?
               "Exception: #{Byebug.last_exception.inspect}" : "" + "\n" if
    @state.context.dead?
  print "Program stopped. "
  info_stop_reason @state.context.stop_reason
end

def info_stack(*args)

def info_stack(*args)
  if not @state.context
    errmsg "info stack not available here.\n"
    return
  end
  print_backtrace
end

def info_stop_reason(stop_reason)

def info_stop_reason(stop_reason)
  case stop_reason
    when :step
      print "It stopped after stepping, next'ing or initial start.\n"
    when :breakpoint
      print("It stopped at a breakpoint.\n")
    when :catchpoint
      print("It stopped at a catchpoint.\n")
    else
      print "unknown reason: %s\n" % @state.context.stop_reason.to_s
  end
end

def info_variables(*args)

def info_variables(*args)
  if not @state.context
    errmsg "info variables not available here.\n"
    return
  end
  obj = debug_eval('self')
  locals = @state.context.frame_locals(@state.frame_pos)
  locals[:self] = @state.context.frame_self(@state.frame_pos)
  locals.keys.sort.each do |name|
    next if name =~ /^__dbg_/ # skip byebug pollution
    ### FIXME: make a common routine
    begin
      s = "#{name} = #{locals[name].inspect}"
    rescue
      begin
        s = "#{name} = #{locals[name].to_s}"
      rescue
        s = "#{name} = *Error in evaluation*"
      end
    end
    pad_with_dots(s)
    s.gsub!('%', '%%')  # protect against printf format strings
    print "#{s}\n"
  end
  var_list(obj.instance_variables, obj.instance_eval{binding()})
  var_class_self
end

def names

def names
  %w(info)
end

def regexp

def regexp
  /^\s* i(?:nfo)? (?:\s+(.*))?$/ix
end