class RubyLsp::Requests::Support::RuboCopDiagnostic

def autocorrect_action

def autocorrect_action
  Interface::CodeAction.new(
    title: "Autocorrect #{@offense.cop_name}",
    kind: Constant::CodeActionKind::QUICK_FIX,
    edit: Interface::WorkspaceEdit.new(
      document_changes: [
        Interface::TextDocumentEdit.new(
          text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
            uri: @uri.to_s,
            version: nil,
          ),
          edits: @offense.correctable? ? offense_replacements : [],
        ),
      ],
    ),
    is_preferred: true,
  )
end

def code_description

def code_description
  doc_url = RuboCopRunner.find_cop_by_name(@offense.cop_name)&.documentation_url
  Interface::CodeDescription.new(href: doc_url) if doc_url
end

def disable_line_action

def disable_line_action
  Interface::CodeAction.new(
    title: "Disable #{@offense.cop_name} for this line",
    kind: Constant::CodeActionKind::QUICK_FIX,
    edit: Interface::WorkspaceEdit.new(
      document_changes: [
        Interface::TextDocumentEdit.new(
          text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
            uri: @uri.to_s,
            version: nil,
          ),
          edits: line_disable_comment,
        ),
      ],
    ),
  )
end

def initialize(document, offense, uri)

def initialize(document, offense, uri)
  @document = document
  @offense = offense
  @uri = uri
end

def length_of_line(line)

def length_of_line(line)
  if @document.encoding == Constant::PositionEncodingKind::UTF16
    line_length = 0
    line.codepoints.each do |codepoint|
      line_length += 1
      if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
        line_length += 1
      end
    end
    line_length
  else
    line.length
  end
end

def line_disable_comment

def line_disable_comment
  new_text = if @offense.source_line.include?(" # rubocop:disable ")
    ",#{@offense.cop_name}"
  else
    " # rubocop:disable #{@offense.cop_name}"
  end
  eol = Interface::Position.new(
    line: @offense.line - 1,
    character: length_of_line(@offense.source_line),
  )
  # TODO: fails for multiline strings - may be preferable to use block
  # comments to disable some offenses
  inline_comment = Interface::TextEdit.new(
    range: Interface::Range.new(start: eol, end: eol),
    new_text: new_text,
  )
  [inline_comment]
end

def message

def message
  message  = @offense.message
  message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
  message
end

def offense_replacements

def offense_replacements
  @offense.corrector.as_replacements.map do |range, replacement|
    Interface::TextEdit.new(
      range: Interface::Range.new(
        start: Interface::Position.new(line: range.line - 1, character: range.column),
        end: Interface::Position.new(line: range.last_line - 1, character: range.last_column),
      ),
      new_text: replacement,
    )
  end
end

def severity

def severity
  RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
end

def to_lsp_code_actions

def to_lsp_code_actions
  code_actions = []
  code_actions << autocorrect_action if @offense.correctable?
  code_actions << disable_line_action
  code_actions
end

def to_lsp_diagnostic

def to_lsp_diagnostic
  Interface::Diagnostic.new(
    message: message,
    source: "RuboCop",
    code: @offense.cop_name,
    code_description: code_description,
    severity: severity,
    range: Interface::Range.new(
      start: Interface::Position.new(
        line: @offense.line - 1,
        character: @offense.column,
      ),
      end: Interface::Position.new(
        line: @offense.last_line - 1,
        character: @offense.last_column,
      ),
    ),
    data: {
      correctable: @offense.correctable?,
      code_actions: to_lsp_code_actions,
    },
  )
end