class RuboCop::Cop::Layout::ElseAlignment

end
code
else
code
if something
# good
end
code
elsif something
code
if something
# bad
end
code
else
code
if something
# bad
@example
alignment of end.
are special cases when they should follow the same rules as the
be aligned with an if/unless/while/until/begin/def/rescue keyword, but there
Checks the alignment of else keywords. Normally they should

def assignment_node(node)

def assignment_node(node)
  assignment_node = node.ancestors.first
  return unless assignment_node&.assignment?
  assignment_node
end

def autocorrect(corrector, node)

def autocorrect(corrector, node)
  AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
end

def base_for_method_definition(node)

def base_for_method_definition(node)
  parent = node.parent
  if parent&.send_type?
    parent.loc.selector # For example "private def ..."
  else
    node.loc.keyword
  end
end

def base_range_of_if(node, base)

def base_range_of_if(node, base)
  if base
    base.source_range
  else
    lineage = [node, *node.each_ancestor(:if)]
    lineage.find { |parent| parent.if? || parent.unless? }.loc.keyword
  end
end

def base_range_of_rescue(node)

def base_range_of_rescue(node)
  parent = node.parent
  parent = parent.parent if parent.ensure_type?
  case parent.type
  when :def, :defs then base_for_method_definition(parent)
  when :kwbegin then parent.loc.begin
  when :block
    assignment_node = assignment_node(parent)
    if same_line?(parent, assignment_node)
      assignment_node.source_range
    else
      parent.send_node.source_range
    end
  else node.loc.keyword
  end
end

def check_alignment(base_range, else_range)

def check_alignment(base_range, else_range)
  return unless begins_its_line?(else_range)
  @column_delta = column_offset_between(base_range, else_range)
  return if @column_delta.zero?
  message = format(
    MSG,
    else_range: else_range.source,
    base_range: base_range.source[/^\S*/]
  )
  add_offense(else_range, message: message) do |corrector|
    autocorrect(corrector, else_range)
  end
end

def check_assignment(node, rhs)

def check_assignment(node, rhs)
  # If there are method calls chained to the right hand side of the
  # assignment, we let rhs be the receiver of those method calls before
  # we check its indentation.
  rhs = first_part_of_call_chain(rhs)
  return unless rhs
  end_config = config.for_cop('Layout/EndAlignment')
  style = end_config['EnforcedStyleAlignWith'] || 'keyword'
  base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs
  return unless rhs.if_type?
  check_nested(rhs, base)
end

def check_nested(node, base)

def check_nested(node, base)
  on_if(node, base)
  ignore_node(node)
end

def on_case(node)

def on_case(node)
  return unless node.else?
  check_alignment(node.when_branches.last.loc.keyword, node.loc.else)
end

def on_case_match(node)

def on_case_match(node)
  return unless node.else?
  check_alignment(node.in_pattern_branches.last.loc.keyword, node.loc.else)
end

def on_if(node, base = nil)

def on_if(node, base = nil)
  return if ignored_node?(node)
  return unless node.else? && begins_its_line?(node.loc.else)
  check_alignment(base_range_of_if(node, base), node.loc.else)
  return unless node.elsif_conditional?
  check_nested(node.else_branch, base)
end

def on_rescue(node)

def on_rescue(node)
  return unless node.loc.respond_to?(:else) && node.loc.else
  check_alignment(base_range_of_rescue(node), node.loc.else)
end