class RuboCop::Cop::Style::AlignHash
literal are aligned.
Here we check if the keys, separators, and values of a multi-line hash
def adjust(corrector, delta, range)
def adjust(corrector, delta, range) if delta > 0 corrector.insert_before(range, ' ' * delta) elsif delta < 0 range = Parser::Source::Range.new(range.source_buffer, range.begin_pos - delta.abs, range.begin_pos) corrector.remove(range) end end
def alignment_for(pair)
def alignment_for(pair) if pair.loc.operator.is?('=>') @alignment_for_hash_rockets else @alignment_for_colons end end
def autocorrect(node)
def autocorrect(node) # We can't use the instance variable inside the lambda. That would # just give each lambda the same reference and they would all get the # last value of each. Some local variables fix the problem. key_delta = @column_deltas[:key] || 0 separator_delta = @column_deltas[:separator] || 0 value_delta = @column_deltas[:value] || 0 key, value = *node key_column = key.source_range.column key_delta = -key_column if key_delta < -key_column lambda do |corrector| if value.nil? adjust(corrector, key_delta, node.source_range) else adjust(corrector, key_delta, key.source_range) adjust(corrector, separator_delta, node.loc.operator) adjust(corrector, value_delta, value.source_range) end end end
def check_pairs(node)
def check_pairs(node) first_pair = node.children.first @column_deltas = alignment_for(first_pair) .deltas_for_first_pair(first_pair, node) add_offense(first_pair, :expression) unless good_alignment? node.children.each do |current| @column_deltas = alignment_for(current).deltas(first_pair, current) add_offense(current, :expression) unless good_alignment? end end
def explicit_hash?(node)
def explicit_hash?(node) node.loc.begin end
def good_alignment?
def good_alignment? @column_deltas.values.compact.none? { |v| v != 0 } end
def hash?(node)
def hash?(node) node.respond_to?(:type) && node.type == :hash end
def ignore_last_argument_hash?(node)
def ignore_last_argument_hash?(node) case cop_config['EnforcedLastArgumentHashStyle'] when 'always_inspect' then false when 'always_ignore' then true when 'ignore_explicit' then explicit_hash?(node) when 'ignore_implicit' then !explicit_hash?(node) end end
def new_alignment(key)
def new_alignment(key) case cop_config[key] when 'key' then KeyAlignment.new when 'table' then TableAlignment.new when 'separator' then SeparatorAlignment.new else raise "Unknown #{key}: #{cop_config[key]}" end end
def on_hash(node)
def on_hash(node) return if ignored_node?(node) return if node.children.empty? return unless node.multiline? @alignment_for_hash_rockets ||= new_alignment('EnforcedHashRocketStyle') @alignment_for_colons ||= new_alignment('EnforcedColonStyle') unless @alignment_for_hash_rockets.checkable_layout(node) && @alignment_for_colons.checkable_layout(node) return end check_pairs(node) end
def on_send(node)
def on_send(node) return unless (last_child = node.children.last) && hash?(last_child) && ignore_last_argument_hash?(last_child) ignore_node(last_child) end