lib/rubocop/ast/node/mixin/hash_element_node.rb
# frozen_string_literal: true
module RuboCop
module AST
# Common functionality for nodes that can be used as hash elements:
# `pair`, `kwsplat`
module HashElementNode
# Returns the key of this `hash` element.
#
# @note For keyword splats, this returns the whole node
#
# @return [Node] the key of the hash element
def key
node_parts[0]
end
# Returns the value of this `hash` element.
#
# @note For keyword splats, this returns the whole node
#
# @return [Node] the value of the hash element
def value
node_parts[1]
end
# Checks whether this `hash` element is on the same line as `other`.
#
# @note A multiline element is considered to be on the same line if it
# shares any of its lines with `other`
#
# @return [Boolean] whether this element is on the same line as `other`
def same_line?(other)
loc.last_line == other.loc.line || loc.line == other.loc.last_line
end
# Returns the delta between this pair's key and the argument pair's.
#
# @note Keys on the same line always return a delta of 0
# @note Keyword splats always return a delta of 0 for right alignment
#
# @param [Symbol] alignment whether to check the left or right side
# @return [Integer] the delta between the two keys
def key_delta(other, alignment = :left)
HashElementDelta.new(self, other).key_delta(alignment)
end
# Returns the delta between this element's value and the argument's.
#
# @note Keyword splats always return a delta of 0
#
# @return [Integer] the delta between the two values
def value_delta(other)
HashElementDelta.new(self, other).value_delta
end
# Returns the delta between this element's delimiter and the argument's.
#
# @note Pairs with different delimiter styles return a delta of 0
#
# @return [Integer] the delta between the two delimiters
def delimiter_delta(other)
HashElementDelta.new(self, other).delimiter_delta
end
# A helper class for comparing the positions of different parts of a
# `pair` node.
class HashElementDelta
def initialize(first, second)
@first = first
@second = second
raise ArgumentError unless valid_argument_types?
end
def key_delta(alignment = :left)
return 0 if first.same_line?(second)
return 0 if keyword_splat? && alignment == :right
delta(first.key.loc, second.key.loc, alignment)
end
def value_delta
return 0 if first.same_line?(second)
return 0 if keyword_splat?
delta(first.value.loc, second.value.loc)
end
def delimiter_delta
return 0 if first.same_line?(second)
return 0 if first.delimiter != second.delimiter
delta(first.loc.operator, second.loc.operator)
end
private
attr_reader :first, :second
def valid_argument_types?
[first, second].all? do |argument|
argument.pair_type? || argument.kwsplat_type?
end
end
def delta(first, second, alignment = :left)
case alignment
when :left
first.column - second.column
when :right
first.last_column - second.last_column
else
0
end
end
def keyword_splat?
[first, second].any?(&:kwsplat_type?)
end
end
private_constant :HashElementDelta
end
end
end