class Rubocop::Cop::Lint::EndAlignment

end
i
variable = lambda do |i|
end
variable = if true
@example
is defined.
For blocks - with the start of the expression where the block
of the keyword.
For keywords (if, def, etc.) the end is aligned with the start
This cop checks whether the end keywords are aligned properly.

def already_processed_node?(node)

def already_processed_node?(node)
  @inspected_blocks.include?(node)
end

def block_is_on_next_line?(begin_node, block_node)

def block_is_on_next_line?(begin_node, block_node)
  begin_node.loc.line != block_node.loc.line
end

def check(node)

def check(node)
  # discard modifier forms of if/while/until
  return unless node.loc.end
  kw_loc = node.loc.keyword
  end_loc = node.loc.end
  if kw_loc.line != end_loc.line && kw_loc.column != end_loc.column
    add_offence(:warning,
                end_loc,
                sprintf(MSG, end_loc.line, end_loc.column,
                        kw_loc.source, kw_loc.line, kw_loc.column))
  end
end

def check_block_alignment(start_loc, block_loc)

def check_block_alignment(start_loc, block_loc)
  end_loc = block_loc.end
  if block_loc.begin.line != end_loc.line &&
       start_loc.column != end_loc.column
    add_offence(:warning,
                end_loc,
                sprintf(MSG, end_loc.line, end_loc.column,
                        start_loc.source.lines.to_a.first.chomp,
                        start_loc.line, start_loc.column))
  end
end

def initialize

def initialize
  super
  @inspected_blocks = []
end

def inspect(source_buffer, source, tokens, ast, comments)

def inspect(source_buffer, source, tokens, ast, comments)
  @inspected_blocks = []
  super
end

def on_and(node)

def on_and(node)
  return if already_processed_node?(node)
  _left, right = *node
  if right.type == :block
    check_block_alignment(node.loc.expression, right.loc)
    @inspected_blocks << right
  end
  super
end

def on_block(node)

def on_block(node)
  return if already_processed_node?(node)
  check_block_alignment(node.loc.expression, node.loc)
  super
end

def on_casgn(node)

def on_casgn(node)
  _, _, children = *node
  process_block_assignment(node, children)
  super
end

def on_class(node)

def on_class(node)
  check(node)
  super
end

def on_def(node)

def on_def(node)
  check(node)
  super
end

def on_defs(node)

def on_defs(node)
  check(node)
  super
end

def on_if(node)

def on_if(node)
  check(node) if node.loc.respond_to?(:end)
  super
end

def on_lvasgn(node)

def on_lvasgn(node)
  _, children = *node
  process_block_assignment(node, children)
  super
end

def on_masgn(node)

def on_masgn(node)
  variables, args = *node
  process_block_assignment(variables, args)
  super
end

def on_module(node)

def on_module(node)
  check(node)
  super
end

def on_op_asgn(node)

def on_op_asgn(node)
  variable, _op, args = *node
  process_block_assignment(variable, args)
  super
end

def on_send(node)

def on_send(node)
  _receiver, _method, *args = *node
  process_block_assignment(node, args.last)
  super
end

def on_until(node)

def on_until(node)
  check(node)
  super
end

def on_while(node)

def on_while(node)
  check(node)
  super
end

def process_block_assignment(begin_node, block_node)

def process_block_assignment(begin_node, block_node)
  return unless block_node
  return if already_processed_node?(block_node)
  while block_node.type == :send
    receiver, _method, args = *block_node
    if receiver && [:block, :send].include?(receiver.type)
      block_node = receiver
    elsif args && [:block, :send].include?(args.type)
      block_node = args
    else
      break
    end
  end
  if block_node.type == :block
    # Align with the expression that is on the same line
    # where the block is defined
    return if block_is_on_next_line?(begin_node, block_node)
    @inspected_blocks << block_node
    check_block_alignment(begin_node.loc.expression, block_node.loc)
  end
end