110 lines
3.2 KiB
Ruby
110 lines
3.2 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
require 'rubocop'
|
||
|
|
||
|
module RuboCop
|
||
|
module Formatter
|
||
|
class GracefulFormatter < ::RuboCop::Formatter::ProgressFormatter
|
||
|
CONFIG_DETAILS_KEY = 'Details'
|
||
|
CONFIG_DETAILS_VALUE = 'grace period'
|
||
|
|
||
|
class << self
|
||
|
attr_accessor :active_offenses
|
||
|
end
|
||
|
|
||
|
def started(...)
|
||
|
super
|
||
|
|
||
|
self.class.active_offenses = 0
|
||
|
|
||
|
@silenced_offenses_for_files = {}
|
||
|
@config = RuboCop::ConfigStore.new.for_pwd
|
||
|
end
|
||
|
|
||
|
def file_finished(file, offenses)
|
||
|
silenced_offenses, active_offenses = offenses.partition { silenced?(_1) }
|
||
|
|
||
|
@silenced_offenses_for_files[file] = silenced_offenses if silenced_offenses.any?
|
||
|
|
||
|
super(file, active_offenses)
|
||
|
end
|
||
|
|
||
|
def finished(inspected_files)
|
||
|
# See the note below why are using this ivar in the first place.
|
||
|
unless defined?(@total_offense_count)
|
||
|
raise <<~MESSAGE
|
||
|
RuboCop has changed its internals and the instance variable
|
||
|
`@total_offense_count` is no longer defined but we were relying on it.
|
||
|
|
||
|
Please change the implementation.
|
||
|
|
||
|
See https://github.com/rubocop/rubocop/blob/65a757b0f/lib/rubocop/formatter/simple_text_formatter.rb#L24
|
||
|
MESSAGE
|
||
|
end
|
||
|
|
||
|
super
|
||
|
|
||
|
# Internally, RuboCop has no notion of "silenced offenses". We cannot
|
||
|
# override this meaning in a formatter that's why we track what we
|
||
|
# consider to be an active offense.
|
||
|
# This is needed for `adjusted_exit_status` method below.
|
||
|
self.class.active_offenses = @total_offense_count
|
||
|
|
||
|
report_silenced_offenses(inspected_files)
|
||
|
end
|
||
|
|
||
|
# We consider this run a success without any active offenses.
|
||
|
def self.adjusted_exit_status(status)
|
||
|
return status unless status == RuboCop::CLI::STATUS_OFFENSES
|
||
|
return RuboCop::CLI::STATUS_SUCCESS if active_offenses == 0
|
||
|
|
||
|
status
|
||
|
end
|
||
|
|
||
|
def self.grace_period?(cop_name, config)
|
||
|
details = config[CONFIG_DETAILS_KEY]
|
||
|
return false unless details
|
||
|
return true if details == CONFIG_DETAILS_VALUE
|
||
|
|
||
|
warn "#{cop_name}: Unhandled value #{details.inspect} for `Details` key."
|
||
|
|
||
|
false
|
||
|
end
|
||
|
|
||
|
def self.grace_period_key_value
|
||
|
"#{CONFIG_DETAILS_KEY}: #{CONFIG_DETAILS_VALUE}"
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def silenced?(offense)
|
||
|
cop_config = @config.for_cop(offense.cop_name)
|
||
|
|
||
|
self.class.grace_period?(offense.cop_name, cop_config)
|
||
|
end
|
||
|
|
||
|
def report_silenced_offenses(inspected_files)
|
||
|
return if @silenced_offenses_for_files.empty?
|
||
|
|
||
|
output.puts
|
||
|
output.puts 'Silenced offenses:'
|
||
|
output.puts
|
||
|
|
||
|
@silenced_offenses_for_files.each do |file, offenses|
|
||
|
report_file(file, offenses)
|
||
|
end
|
||
|
|
||
|
silenced_offense_count = @silenced_offenses_for_files.values.sum(&:size)
|
||
|
silenced_text = colorize("#{silenced_offense_count} offenses", :yellow)
|
||
|
|
||
|
output.puts
|
||
|
output.puts "#{inspected_files.size} files inspected, #{silenced_text} silenced"
|
||
|
end
|
||
|
|
||
|
def report_file_as_mark(_offenses)
|
||
|
# Skip progress bar. No dots. No C/Ws.
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|