Skip to content

Commit

Permalink
[Fix #9927] Indent hash values in LineEndStringConcatenationIndentation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jonas054 committed Aug 8, 2021
1 parent 37134fb commit acd01be
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 37 deletions.
1 change: 1 addition & 0 deletions 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][])
47 changes: 33 additions & 14 deletions lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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|
Expand All @@ -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
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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' \
Expand Down Expand Up @@ -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) \
Expand Down

0 comments on commit acd01be

Please sign in to comment.