prontolabs/pronto-rubocop

View on GitHub
lib/pronto/rubocop/offense_line.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

module Pronto
  class Rubocop < Runner
    class OffenseLine
      def initialize(patch_cop, offense, line)
        @patch_cop = patch_cop
        @offense = offense
        @line = line
      end

      def message
        Message.new(path, line, level, message_text, nil, Pronto::Rubocop)
      end

      private

      attr_reader :patch_cop, :offense, :line

      def path
        line.patch.delta.new_file[:path]
      end

      def processed_source
        patch_cop.processed_source
      end

      def message_text
        return offense.message unless suggestion_text

        "#{offense.message}\n\n```suggestion\n#{suggestion_text}```"
      end

      def suggestion_text
        return unless patch_cop.runner.pronto_rubocop_config['suggestions']
        return if corrections_count.zero?
        return if corrector.nil?  # possible after optimisation in https://github.com/rubocop/rubocop/pull/11264
        return if differing_lines_count != corrections_count

        @suggestion_text ||= corrected_lines[offense.line - 1]
      end

      def corrected_lines
        @corrected_lines ||= corrector.rewrite.lines
      end

      def differing_lines_count
        original_lines.each_with_index.count do |line, index|
          line != corrected_lines[index]
        end
      end

      def original_lines
        processed_source.lines.join("\n").lines
      end

      if ::RuboCop::Cop::Team.respond_to?(:mobilize) && ::RuboCop::Cop::Team.public_method_defined?(:investigate)
        # rubocop >= 0.87.0 has both mobilize and public investigate method
        MOBILIZE = :mobilize
        # rubocop 1.30.0 renamed from auto_correct to autocorrect
        AUTOCORRECT = Gem::Version.new(::RuboCop::Version::STRING) >= Gem::Version.new("1.30.0") ? :autocorrect : :auto_correct

        def report
          @report ||= autocorrect_team.investigate(processed_source).cop_reports.first
        end

        def corrector
          report.corrector
        end

        def corrections_count
          # Some lines may contain more than one offense
          report.offenses.map(&:line).uniq.size
        end
      else
        # rubocop 0.85.x and 0.86.0 have mobilize, older versions don't
        MOBILIZE = ::RuboCop::Cop::Team.respond_to?(:mobilize) ? :mobilize : :new
        AUTOCORRECT = :auto_correct

        def corrector
          @corrector ||= begin
            autocorrect_team.inspect_file(processed_source)
            corrector = RuboCop::Cop::Corrector.new(processed_source.buffer)
            corrector.corrections.concat(autocorrect_team.cops.first.corrections)
            corrector
          end
        end

        def corrections_count
          @corrections_count ||= corrector.corrections.count
        end
      end

      def autocorrect_team
        @autocorrect_team ||=
          ::RuboCop::Cop::Team.send(MOBILIZE,
                                    ::RuboCop::Cop::Registry.new([cop_class]),
                                    patch_cop.rubocop_config,
                                    **{ AUTOCORRECT => true, stdin: true })
      end

      def cop_class
        patch_cop.registry.find_by_cop_name(offense.cop_name)
      end

      def level
        severities.fetch(offense.severity.name)
      end

      def severities
        @severities ||= DEFAULT_SEVERITIES.merge(config_severities)
      end

      def config_severities
        patch_cop
          .runner
          .pronto_rubocop_config
          .fetch('severities', {})
          .map { |k, v| [k.to_sym, v.to_sym] }
          .to_h
      end

      DEFAULT_SEVERITIES = {
        info: :info,
        refactor: :warning,
        convention: :warning,
        warning: :warning,
        error: :error,
        fatal: :fatal
      }.freeze

      private_constant :DEFAULT_SEVERITIES
    end
  end
end