From acd01be6b9561b9060646ad02f9e2669fa04b56e Mon Sep 17 00:00:00 2001 From: Jonas Arvidsson Date: Sun, 8 Aug 2021 10:06:39 +0200 Subject: [PATCH] [Fix #9927] Indent hash values in LineEndStringConcatenationIndentation Make Layout/LineEndStringConcatenationIndentation work like Layout/MultilineOperationIndentation when EnforcedStyle is indented. This means that concatenated strings in hash literals shall be indented, just like other concatenated strings. --- changelog/fix_indent_hash_values_in.md | 1 + ...ne_end_string_concatenation_indentation.rb | 47 +++++++++---- ...d_string_concatenation_indentation_spec.rb | 69 ++++++++++++------- 3 files changed, 80 insertions(+), 37 deletions(-) create mode 100644 changelog/fix_indent_hash_values_in.md diff --git a/changelog/fix_indent_hash_values_in.md b/changelog/fix_indent_hash_values_in.md new file mode 100644 index 00000000000..8285adc1311 --- /dev/null +++ b/changelog/fix_indent_hash_values_in.md @@ -0,0 +1 @@ +* [#9927](https://github.com/rubocop/rubocop/issues/9927): Indent hash values in `Layout/LineEndStringConcatenationIndentation`. ([@jonas054][]) diff --git a/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb b/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb index 6c9b917f909..1f32c3ff91f 100644 --- a/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +++ b/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb @@ -11,8 +11,7 @@ module Layout # concatenated string parts shall be indented regardless of `EnforcedStyle` configuration. # # If `EnforcedStyle: indented` is set, it's the second line that shall be indented one step - # more than the first line. Lines 3 and forward shall be aligned with line 2. Here too there - # are exceptions. Values in a hash literal are always aligned. + # more than the first line. Lines 3 and forward shall be aligned with line 2. # # @example # # bad @@ -34,29 +33,44 @@ module Layout # 'z' # end # - # my_hash = { - # first: 'a message' \ - # 'in two parts' - # } - # # @example EnforcedStyle: aligned (default) # # bad # puts 'x' \ # 'y' # + # my_hash = { + # first: 'a message' \ + # 'in two parts' + # } + # # # good # puts 'x' \ # 'y' # + # my_hash = { + # first: 'a message' \ + # 'in two parts' + # } + # # @example EnforcedStyle: indented # # bad # result = 'x' \ # 'y' # + # my_hash = { + # first: 'a message' \ + # 'in two parts' + # } + # # # good # result = 'x' \ # 'y' # + # my_hash = { + # first: 'a message' \ + # 'in two parts' + # } + # class LineEndStringConcatenationIndentation < Base include ConfigurableEnforcedStyle include Alignment @@ -70,7 +84,7 @@ def on_dstr(node) return unless strings_concatenated_with_backslash?(node) children = node.children - if style == :aligned && !always_indented?(node) || always_aligned?(node) + if style == :aligned && !always_indented?(node) check_aligned(children, 1) else check_indented(children) @@ -94,10 +108,6 @@ def always_indented?(dstr_node) PARENT_TYPES_FOR_INDENTED.include?(dstr_node.parent&.type) end - def always_aligned?(dstr_node) - dstr_node.parent&.pair_type? - end - def check_aligned(children, start_index) base_column = children[start_index - 1].loc.column children[start_index..-1].each do |child| @@ -108,11 +118,20 @@ def check_aligned(children, start_index) end def check_indented(children) - base_column = children[0].source_range.source_line =~ /\S/ - @column_delta = base_column + configured_indentation_width - children[1].loc.column + @column_delta = base_column(children[0]) + configured_indentation_width - + children[1].loc.column add_offense_and_correction(children[1], MSG_INDENT) if @column_delta != 0 end + def base_column(child) + grandparent = child.parent.parent + if grandparent&.type == :pair + grandparent.loc.column + else + child.source_range.source_line =~ /\S/ + end + end + def add_offense_and_correction(node, message) add_offense(node, message: message) { |corrector| autocorrect(corrector, node) } end diff --git a/spec/rubocop/cop/layout/line_end_string_concatenation_indentation_spec.rb b/spec/rubocop/cop/layout/line_end_string_concatenation_indentation_spec.rb index 26cbe7b5bac..b13f2d5e70b 100644 --- a/spec/rubocop/cop/layout/line_end_string_concatenation_indentation_spec.rb +++ b/spec/rubocop/cop/layout/line_end_string_concatenation_indentation_spec.rb @@ -48,29 +48,6 @@ RUBY end - it 'registers an offense for unaligned strings in hash literal values' do - expect_offense(<<~'RUBY') - MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ - 'they span more than one line.', - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. - SeparatorAlignment => 'Align the separators of a hash ' \ - 'literal if they span more than one line.', - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. - TableAlignment => 'Align the keys and values of a hash ' \ - 'literal if they span more than one line.' }.freeze - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. - RUBY - - expect_correction(<<~'RUBY') - MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ - 'they span more than one line.', - SeparatorAlignment => 'Align the separators of a hash ' \ - 'literal if they span more than one line.', - TableAlignment => 'Align the keys and values of a hash ' \ - 'literal if they span more than one line.' }.freeze - RUBY - end - it 'accepts indented strings in implicit return statement of a method definition' do expect_no_offenses(<<~'RUBY') def some_method @@ -216,6 +193,29 @@ def some_method end end + it 'registers an offense for unaligned strings in hash literal values' do + expect_offense(<<~'RUBY') + MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ + 'they span more than one line.', + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. + SeparatorAlignment => 'Align the separators of a hash ' \ + 'literal if they span more than one line.', + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. + TableAlignment => 'Align the keys and values of a hash ' \ + 'literal if they span more than one line.' }.freeze + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align parts of a string concatenated with backslash. + RUBY + + expect_correction(<<~'RUBY') + MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ + 'they span more than one line.', + SeparatorAlignment => 'Align the separators of a hash ' \ + 'literal if they span more than one line.', + TableAlignment => 'Align the keys and values of a hash ' \ + 'literal if they span more than one line.' }.freeze + RUBY + end + it 'registers an offense for indented string' do expect_offense(<<~'RUBY') puts 'a' \ @@ -286,6 +286,29 @@ def some_method end end + it 'registers an offense for aligned strings in hash literal values' do + expect_offense(<<~'RUBY') + MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ + 'they span more than one line.', + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Indent the first part of a string concatenated with backslash. + SeparatorAlignment => 'Align the separators of a hash ' \ + 'literal if they span more than one line.', + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Indent the first part of a string concatenated with backslash. + TableAlignment => 'Align the keys and values of a hash ' \ + 'literal if they span more than one line.' }.freeze + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Indent the first part of a string concatenated with backslash. + RUBY + + expect_correction(<<~'RUBY') + MESSAGES = { KeyAlignment => 'Align the keys of a hash literal if ' \ + 'they span more than one line.', + SeparatorAlignment => 'Align the separators of a hash ' \ + 'literal if they span more than one line.', + TableAlignment => 'Align the keys and values of a hash ' \ + 'literal if they span more than one line.' }.freeze + RUBY + end + it 'registers an offense for aligned string' do expect_offense(<<~'RUBY') puts %Q(a) \