class RubyLsp::Requests::CodeActionResolve

def refactor_method

: -> (Interface::CodeAction)
def refactor_method
  source_range = @code_action.dig(:data, :range)
  raise EmptySelectionError, "Invalid selection for refactor" if source_range[:start] == source_range[:end]
  start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
  extracted_source = @document.source[start_index...end_index] #: as !nil
  # Find the closest method declaration node, so that we place the refactor in a valid position
  node_context = RubyDocument.locate(
    @document.ast,
    start_index,
    node_types: [Prism::DefNode],
    code_units_cache: @document.code_units_cache,
  )
  closest_node = node_context.node
  unless closest_node
    raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
  end
  target_range = if closest_node.is_a?(Prism::DefNode)
    end_keyword_loc = closest_node.end_keyword_loc
    unless end_keyword_loc
      raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
    end
    end_line = end_keyword_loc.end_line - 1
    character = end_keyword_loc.end_column
    indentation = " " * end_keyword_loc.start_column
    new_method_source = <<~RUBY.chomp
      #{indentation}def #{NEW_METHOD_NAME}
      #{indentation}  #{extracted_source}
      #{indentation}end
    RUBY
    {
      start: { line: end_line, character: character },
      end: { line: end_line, character: character },
    }
  else
    new_method_source = <<~RUBY
      #{indentation}def #{NEW_METHOD_NAME}
      #{indentation}  #{extracted_source.gsub("\n", "\n  ")}
      #{indentation}end
    RUBY
    line = [0, source_range.dig(:start, :line) - 1].max
    {
      start: { line: line, character: source_range.dig(:start, :character) },
      end: { line: line, character: source_range.dig(:start, :character) },
    }
  end
  Interface::CodeAction.new(
    title: CodeActions::EXTRACT_TO_METHOD_TITLE,
    edit: Interface::WorkspaceEdit.new(
      document_changes: [
        Interface::TextDocumentEdit.new(
          text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
            uri: @code_action.dig(:data, :uri),
            version: nil,
          ),
          edits: [
            create_text_edit(target_range, new_method_source),
            create_text_edit(source_range, NEW_METHOD_NAME),
          ],
        ),
      ],
    ),
  )
end