class RuboCop::Cop::Style::MixinGrouping

end
extend Qox, Bar
class Foo
# good
end
extend Qox
extend Bar
class Foo
# bad
@example EnforcedStyle: grouped
end
include Bar
include Qox
class Foo
# good
end
include Bar, Qox
class Foo
# bad
@example EnforcedStyle: separated (default)
but it can be configured to enforce grouping them in one declaration.
By default it enforces mixins to be placed in separate declarations,
This cop checks for grouping of mixins in ‘class` and `module` bodies.

def check(send_node)

def check(send_node)
  if separated_style?
    check_separated_style(send_node)
  else
    check_grouped_style(send_node)
  end
end

def check_grouped_style(send_node)

def check_grouped_style(send_node)
  return if sibling_mixins(send_node).size == 1
  message = format(MSG, mixin: send_node.method_name, suffix: 'a single statement')
  add_offense(send_node, message: message) do |corrector|
    range = send_node.loc.expression
    mixins = sibling_mixins(send_node)
    if send_node == mixins.first
      correction = group_mixins(send_node, mixins)
    else
      range = range_to_remove_for_subsequent_mixin(mixins, send_node)
      correction = ''
    end
    corrector.replace(range, correction)
  end
end

def check_separated_style(send_node)

def check_separated_style(send_node)
  return if send_node.arguments.one?
  message = format(MSG, mixin: send_node.method_name, suffix: 'separate statements')
  add_offense(send_node, message: message) do |corrector|
    range = send_node.loc.expression
    correction = separate_mixins(send_node)
    corrector.replace(range, correction)
  end
end

def group_mixins(node, mixins)

def group_mixins(node, mixins)
  mixin_names = mixins.reverse.flat_map { |mixin| mixin.arguments.map(&:source) }
  "#{node.method_name} #{mixin_names.join(', ')}"
end

def grouped_style?

def grouped_style?
  style == :grouped
end

def on_class(node)

def on_class(node)
  begin_node = node.child_nodes.find(&:begin_type?) || node
  begin_node.each_child_node(:send).select(&:macro?).each do |macro|
    next unless MIXIN_METHODS.include?(macro.method_name)
    check(macro)
  end
end

def range_to_remove_for_subsequent_mixin(mixins, node)

def range_to_remove_for_subsequent_mixin(mixins, node)
  range = node.loc.expression
  prev_mixin = mixins.each_cons(2) { |m, n| break m if n == node }
  between = prev_mixin.loc.expression.end.join(range.begin)
  # if separated from previous mixin with only whitespace?
  unless /\S/.match?(between.source)
    range = range.join(between) # then remove that too
  end
  range
end

def separate_mixins(node)

def separate_mixins(node)
  arguments = node.arguments.reverse
  mixins = ["#{node.method_name} #{arguments.first.source}"]
  arguments[1..-1].inject(mixins) do |replacement, arg|
    replacement << "#{indent(node)}#{node.method_name} #{arg.source}"
  end.join("\n")
end

def separated_style?

def separated_style?
  style == :separated
end

def sibling_mixins(send_node)

def sibling_mixins(send_node)
  siblings = send_node.parent.each_child_node(:send).select(&:macro?)
  siblings.select { |sibling_node| sibling_node.method?(send_node.method_name) }
end