class Rubocop::CLI

logic.
The CLI is a class responsible of handling all the command line interface

def self.parse(file)

def self.parse(file)
  parser = Parser::CurrentRuby.new
  # On JRuby and Rubinius, there's a risk that we hang in
  # tokenize() if we don't set the all errors as fatal flag.
  parser.diagnostics.all_errors_are_fatal = RUBY_ENGINE != 'ruby'
  parser.diagnostics.ignore_warnings      = false
  diagnostics = []
  parser.diagnostics.consumer = lambda do |diagnostic|
    diagnostics << diagnostic
  end
  source_buffer = Parser::Source::Buffer.new(file, 1)
  yield source_buffer
  begin
    ast, comments, tokens = parser.tokenize(source_buffer)
  rescue Parser::SyntaxError # rubocop:disable HandleExceptions
    # All errors are in diagnostics. No need to handle exception.
  end
  if tokens
    tokens = tokens.map do |t|
      type, details = *t
      text, range = *details
      Rubocop::Cop::Token.new(range, type, text)
    end
  end
  syntax_offences = diagnostics.map do |d|
    Cop::Offence.new(d.level, d.location, "#{d.message}",
                     'Syntax')
  end
  source = source_buffer.source.split($RS)
  [ast, comments, tokens, source_buffer, source, syntax_offences]
end

def convert_deprecated_options!(args)

def convert_deprecated_options!(args)
  args.map! do |arg|
    case arg
    when '-e', '--emacs'
      deprecate("#{arg} option", '--format emacs', '1.0.0')
      %w(--format emacs)
    else
      arg
    end
  end.flatten!
end

def deprecate(subject, alternative = nil, version = nil)

def deprecate(subject, alternative = nil, version = nil)
  message =  "#{subject} is deprecated"
  message << " and will be removed in RuboCop #{version}" if version
  message << '.'
  message << " Please use #{alternative} instead." if alternative
  warn message
end

def disabled_lines_in(source)

def disabled_lines_in(source)
  disabled_lines = Hash.new([])
  disabled_section = {}
  regexp = '# rubocop : (%s)\b ((?:\w+,? )+)'.gsub(' ', '\s*')
  section_regexp = '^\s*' + sprintf(regexp, '(?:dis|en)able')
  single_line_regexp = '\S.*' + sprintf(regexp, 'disable')
  source.each_with_index do |line, ix|
    each_mentioned_cop(/#{section_regexp}/, line) do |cop_name, kind|
      disabled_section[cop_name] = (kind == 'disable')
    end
    disabled_section.keys.each do |cop_name|
      disabled_lines[cop_name] += [ix + 1] if disabled_section[cop_name]
    end
    each_mentioned_cop(/#{single_line_regexp}/, line) do |cop_name, kind|
      disabled_lines[cop_name] += [ix + 1] if kind == 'disable'
    end
  end
  disabled_lines
end

def display_error_summary(errors)

def display_error_summary(errors)
  return if errors.empty?
  plural = errors.count > 1 ? 's' : ''
  puts "\n#{errors.count} error#{plural} occurred:".color(:red)
  errors.each { |error| puts error }
  puts 'Errors are usually caused by RuboCop bugs.'
  puts 'Please, report your problems to RuboCop\'s issue tracker.'
  puts 'Mention the following information in the issue report:'
  puts Rubocop::Version.version(true)
end

def each_mentioned_cop(regexp, line)

def each_mentioned_cop(regexp, line)
  match = line.match(regexp)
  if match
    kind, cops = match.captures
    cops = Cop::Cop.all.map(&:cop_name).join(',') if cops.include?('all')
    cops.split(/,\s*/).each { |cop_name| yield cop_name, kind }
  end
end

def excluded_file?(file)

def excluded_file?(file)
  ConfigStore.for(file).file_to_exclude?(file)
end

def formatter_set

def formatter_set
  @formatter_set ||= begin
    set = Formatter::FormatterSet.new(!@options[:silent])
    pairs = @options[:formatters] || [[DEFAULT_FORMATTER]]
    pairs.each do |formatter_key, output_path|
      set.add_formatter(formatter_key, output_path)
    end
    set
  rescue => error
    warn error.message
    exit(1)
  end
end

def handle_error(e, message)

def handle_error(e, message)
  @errors << message
  warn message
  if @options[:debug]
    puts e.message, e.backtrace
  else
    warn 'To see the complete backtrace run rubocop -d.'
  end
end

def initialize

def initialize
  @cops = Cop::Cop.all
  @errors = []
  @options = {}
  ConfigStore.prepare
end

def inspect_file(file)

def inspect_file(file)
  begin
    ast, comments, tokens, source_buffer, source, syntax_offences =
      CLI.parse(file) { |sb| sb.read }
  rescue Encoding::UndefinedConversionError, ArgumentError => e
    handle_error(e, "An error occurred while parsing #{file}.".color(:red))
    return []
  end
  # If we got any syntax errors, return only the syntax offences.
  # Parser may return nil for AST even though there are no syntax errors.
  # e.g. sources which contain only comments
  return syntax_offences unless syntax_offences.empty?
  config = ConfigStore.for(file)
  disabled_lines = disabled_lines_in(source)
  set_config_for_all_cops(config)
  @cops.reduce([]) do |offences, cop_class|
    cop_name = cop_class.cop_name
    if config.cop_enabled?(cop_name)
      cop = setup_cop(cop_class, disabled_lines)
      if !@options[:only] || @options[:only] == cop_name
        begin
          cop.inspect(source_buffer, source, tokens, ast, comments)
        rescue => e
          handle_error(e,
                       "An error occurred while #{cop.name}".color(:red) +
                       " cop was inspecting #{file}.".color(:red))
        end
      end
      offences.concat(cop.offences)
    end
    offences
  end.sort
end

def log_error(e, msg = '')

def log_error(e, msg = '')
  if @options[:debug]
    error_message = "#{e.class}, #{e.message}"
    warn "#{msg}\t#{error_message}"
  end
end

def parse_options(args)

rubocop:disable MethodLength
def parse_options(args)
  convert_deprecated_options!(args)
  OptionParser.new do |opts|
    opts.banner = 'Usage: rubocop [options] [file1, file2, ...]'
    opts.on('-d', '--debug', 'Display debug info.') do |d|
      @options[:debug] = d
    end
    opts.on('-c', '--config FILE', 'Specify configuration file.') do |f|
      @options[:config] = f
      ConfigStore.set_options_config(@options[:config])
    end
    opts.on('--only COP', 'Run just one cop.') do |s|
      @options[:only] = s
      validate_only_option
    end
    opts.on('-f', '--format FORMATTER',
            'Choose an output formatter. This option',
            'can be specified multiple times to enable',
            'multiple formatters at the same time.',
            '  [p]rogress (default)',
            '  [s]imple',
            '  [c]lang',
            '  [e]macs',
            '  [j]son',
            '  custom formatter class name') do |key|
      @options[:formatters] ||= []
      @options[:formatters] << [key]
    end
    opts.on('-o', '--out FILE',
            'Write output to a file instead of STDOUT.',
            'This option applies to the previously',
            'specified --format, or the default format',
            'if no format is specified.') do |path|
      @options[:formatters] ||= [[DEFAULT_FORMATTER]]
      @options[:formatters].last << path
    end
    opts.on('-r', '--require FILE', 'Require Ruby file.') do |f|
      require f
    end
    opts.on('-R', '--rails', 'Run extra Rails cops.') do |r|
      @options[:rails] = r
    end
    opts.on('-l', '--lint', 'Run only lint cops.') do |l|
      @options[:lint] = l
    end
    opts.on('-a', '--auto-correct', 'Auto-correct offences.') do |a|
      @options[:autocorrect] = a
    end
    opts.on('-s', '--silent', 'Silence summary.') do |s|
      @options[:silent] = s
    end
    opts.on('-n', '--no-color', 'Disable color output.') do |s|
      Sickill::Rainbow.enabled = false
    end
    opts.on('-v', '--version', 'Display version.') do
      puts Rubocop::Version.version(false)
      exit(0)
    end
    opts.on('-V', '--verbose-version', 'Display verbose version.') do
      puts Rubocop::Version.version(true)
      exit(0)
    end
  end.parse!(args)
end

def ruby_files(root = Dir.pwd)

Returns:
  • (Array) - Array of filenames

Parameters:
  • root () -- Root directory under which to search for ruby source files
def ruby_files(root = Dir.pwd)
  files = Dir["#{root}/**/*"].select { |file| FileTest.file?(file) }
  rb = []
  rb += files.select { |file| File.extname(file) == '.rb' }
  rb += files.select do |file|
    if File.extname(file) == '' && !excluded_file?(file)
      begin
        File.open(file) { |f| f.readline } =~ /#!.*ruby/
      rescue EOFError, ArgumentError => e
        log_error(e, "Unprocessable file #{file.inspect}: ")
        false
      end
    end
  end
  rb += files.select do |file|
    config = ConfigStore.for(file)
    config.file_to_include?(file)
  end
  rb.reject { |file| excluded_file?(file) }.uniq
end

def run(args = ARGV)

Returns:
  • (Fixnum) - UNIX exit code
def run(args = ARGV)
  trap_interrupt
  begin
    parse_options(args)
  rescue => e
    $stderr.puts e.message
    return 1
  end
  # filter out Rails cops unless requested
  @cops.reject!(&:rails?) unless @options[:rails]
  # filter out style cops when --lint is passed
  @cops.select!(&:lint?) if @options[:lint]
  target_files = target_files(args)
  target_files.each(&:freeze).freeze
  inspected_files = []
  any_failed = false
  formatter_set.started(target_files)
  target_files.each do |file|
    break if wants_to_quit?
    puts "Scanning #{file}" if @options[:debug]
    formatter_set.file_started(file, {})
    offences = inspect_file(file)
    any_failed = true unless offences.empty?
    inspected_files << file
    formatter_set.file_finished(file, offences.freeze)
  end
  formatter_set.finished(inspected_files.freeze)
  formatter_set.close_output_files
  display_error_summary(@errors) unless @options[:silent]
  !any_failed && !wants_to_quit ? 0 : 1
end

def set_config_for_all_cops(config)

def set_config_for_all_cops(config)
  @cops.each do |cop_class|
    cop_class.config = config.for_cop(cop_class.cop_name)
  end
end

def setup_cop(cop_class, disabled_lines = nil)

def setup_cop(cop_class, disabled_lines = nil)
  cop = cop_class.new
  cop.debug = @options[:debug]
  cop.autocorrect = @options[:autocorrect]
  cop.disabled_lines = disabled_lines[cop_class.cop_name] if disabled_lines
  cop
end

def target_files(args)

Returns:
  • (Array) - array of filenames
def target_files(args)
  return ruby_files if args.empty?
  files = []
  args.each do |target|
    if File.directory?(target)
      files += ruby_files(target.chomp(File::SEPARATOR))
    elsif target =~ /\*/
      files += Dir[target]
    else
      files << target
    end
  end
  files.map { |f| File.expand_path(f) }.uniq
end

def trap_interrupt

def trap_interrupt
  Signal.trap('INT') do
    exit!(1) if wants_to_quit?
    self.wants_to_quit = true
    $stderr.puts
    $stderr.puts 'Exiting... Interrupt again to exit immediately.'
  end
end

def validate_only_option

def validate_only_option
  if @cops.none? { |c| c.cop_name == @options[:only] }
    fail ArgumentError, "Unrecognized cop name: #{@options[:only]}."
  end
end