# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleLayout# Checks the indentation of the first key in a hash literal# where the opening brace and the first key are on separate lines. The# other keys' indentations are handled by the HashAlignment cop.## By default, Hash literals that are arguments in a method call with# parentheses, and where the opening curly brace of the hash is on the# same line as the opening parenthesis of the method call, shall have# their first key indented one step (two spaces) more than the position# inside the opening parenthesis.## Other hash literals shall have their first key indented one step more# than the start of the line where the opening curly brace is.## This default style is called 'special_inside_parentheses'. Alternative# styles are 'consistent' and 'align_braces'. Here are examples:## @example EnforcedStyle: special_inside_parentheses (default)# # The `special_inside_parentheses` style enforces that the first key# # in a hash literal where the opening brace and the first key are on# # separate lines is indented one step (two spaces) more than the# # position inside the opening parentheses.## # bad# hash = {# key: :value# }# and_in_a_method_call({# no: :difference# })# takes_multi_pairs_hash(x: {# a: 1,# b: 2# },# y: {# c: 1,# d: 2# })## # good# special_inside_parentheses# hash = {# key: :value# }# but_in_a_method_call({# its_like: :this# })# takes_multi_pairs_hash(x: {# a: 1,# b: 2# },# y: {# c: 1,# d: 2# })## @example EnforcedStyle: consistent# # The `consistent` style enforces that the first key in a hash# # literal where the opening brace and the first key are on# # separate lines is indented the same as a hash literal which is not# # defined inside a method call.## # bad# hash = {# key: :value# }# but_in_a_method_call({# its_like: :this# })## # good# hash = {# key: :value# }# and_in_a_method_call({# no: :difference# })### @example EnforcedStyle: align_braces# # The `align_brackets` style enforces that the opening and closing# # braces are indented to the same position.## # bad# and_now_for_something = {# completely: :different# }# takes_multi_pairs_hash(x: {# a: 1,# b: 2# },# y: {# c: 1,# d: 2# })## # good# and_now_for_something = {# completely: :different# }# takes_multi_pairs_hash(x: {# a: 1,# b: 2# },# y: {# c: 1,# d: 2# })classFirstHashElementIndentation<BaseincludeAlignmentincludeConfigurableEnforcedStyleincludeMultilineElementIndentationextendAutoCorrectorMSG='Use %<configured_indentation_width>d spaces for indentation '\'in a hash, relative to %<base_description>s.'defon_hash(node)check(node,nil)ifnode.loc.beginenddefon_send(node)returnifenforce_first_argument_with_fixed_indentation?each_argument_node(node,:hash)do|hash_node,left_parenthesis|check(hash_node,left_parenthesis)endendaliason_csendon_sendprivatedefautocorrect(corrector,node)AlignmentCorrector.correct(corrector,processed_source,node,@column_delta)enddefbrace_alignment_style:align_bracesenddefcheck(hash_node,left_parenthesis)returnifignored_node?(hash_node)left_brace=hash_node.loc.beginfirst_pair=hash_node.pairs.firstiffirst_pairreturnifsame_line?(first_pair,left_brace)ifseparator_style?(first_pair)check_based_on_longest_key(hash_node,left_brace,left_parenthesis)elsecheck_first(first_pair,left_brace,left_parenthesis,0)endendcheck_right_brace(hash_node.loc.end,first_pair,left_brace,left_parenthesis)enddefcheck_right_brace(right_brace,first_pair,left_brace,left_parenthesis)# if the right brace is on the same line as the last value, acceptreturnif/\S/.match?(right_brace.source_line[0...right_brace.column])expected_column,indent_base_type=indent_base(left_brace,first_pair,left_parenthesis)@column_delta=expected_column-right_brace.columnreturnif@column_delta.zero?message=message_for_right_brace(indent_base_type)add_offense(right_brace,message: message)do|corrector|autocorrect(corrector,right_brace)endenddefseparator_style?(first_pair)separator=first_pair.loc.operatorkey="Enforced#{separator.is?(':')?'Colon':'HashRocket'}Style"config.for_cop('Layout/HashAlignment')[key]=='separator'enddefcheck_based_on_longest_key(hash_node,left_brace,left_parenthesis)key_lengths=hash_node.keys.map{|key|key.source_range.length}check_first(hash_node.pairs.first,left_brace,left_parenthesis,key_lengths.max-key_lengths.first)end# Returns the description of what the correct indentation is based on.defbase_description(indent_base_type)caseindent_base_typewhen:left_brace_or_bracket'the position of the opening brace'when:first_column_after_left_parenthesis'the first position after the preceding left parenthesis'when:parent_hash_key'the parent hash key'else'the start of the line where the left curly brace is'endenddefmessage(base_description)format(MSG,configured_indentation_width: configured_indentation_width,base_description: base_description)enddefmessage_for_right_brace(indent_base_type)caseindent_base_typewhen:left_brace_or_bracket'Indent the right brace the same as the left brace.'when:first_column_after_left_parenthesis'Indent the right brace the same as the first position '\'after the preceding left parenthesis.'when:parent_hash_key'Indent the right brace the same as the parent hash key.'else'Indent the right brace the same as the start of the line '\'where the left brace is.'endenddefenforce_first_argument_with_fixed_indentation?returnfalseunlessargument_alignment_config['Enabled']argument_alignment_config['EnforcedStyle']=='with_fixed_indentation'enddefargument_alignment_configconfig.for_cop('Layout/ArgumentAlignment')endendendendend