diff --git a/changelog/change_preserve_multiline_on_stylesymbolarray.md b/changelog/change_preserve_multiline_on_stylesymbolarray.md new file mode 100644 index 00000000000..3f61727a328 --- /dev/null +++ b/changelog/change_preserve_multiline_on_stylesymbolarray.md @@ -0,0 +1 @@ +* [#10784](https://github.com/rubocop/rubocop/pull/10784): Preserve multiline semantics on `Style/SymbolArray` and `Style/WordArray`. ([@r7kamura][]) diff --git a/lib/rubocop/cop/mixin/percent_array.rb b/lib/rubocop/cop/mixin/percent_array.rb index dfc585fcd0c..faed8fe4341 100644 --- a/lib/rubocop/cop/mixin/percent_array.rb +++ b/lib/rubocop/cop/mixin/percent_array.rb @@ -44,13 +44,26 @@ def check_percent_array(node) no_acceptable_style! if brackets_required bracketed_array = build_bracketed_array(node) - message = format(self.class::ARRAY_MSG, prefer: bracketed_array) + message = build_message_for_bracketed_array(bracketed_array) add_offense(node, message: message) do |corrector| corrector.replace(node, bracketed_array) end end + # @param [String] preferred_array_code + # @return [String] + def build_message_for_bracketed_array(preferred_array_code) + format( + self.class::ARRAY_MSG, + prefer: if preferred_array_code.include?("\n") + 'an array literal `[...]`' + else + "`#{preferred_array_code}`" + end + ) + end + def check_bracketed_array(node, literal_prefix) return if allowed_bracket_array?(node) @@ -63,6 +76,53 @@ def check_bracketed_array(node, literal_prefix) percent_literal_corrector.correct(corrector, node, literal_prefix) end end + + # @param [RuboCop::AST::ArrayNode] node + # @param [Array] elements + # @return [String] + def build_bracketed_array_with_appropriate_whitespace(elements:, node:) + [ + '[', + whitespace_leading(node), + elements.join(",#{whitespace_between(node)}"), + whitespace_trailing(node), + ']' + ].join + end + + # Provides whitespace between elements for building a bracketed array. + # %w[ a b c ] + # ^^^ + # @param [RuboCop::AST::ArrayNode] node + # @return [String] + def whitespace_between(node) + if node.children.length >= 2 + node.source[ + node.children[0].location.expression.end_pos... + node.children[1].location.expression.begin_pos + ] + else + ' ' + end + end + + # Provides leading whitespace for building a bracketed array. + # %w[ a b c ] + # ^^ + # @param [RuboCop::AST::ArrayNode] node + # @return [String] + def whitespace_leading(node) + node.source[node.location.begin.end_pos...node.children[0].location.expression.begin_pos] + end + + # Provides trailing whitespace for building a bracketed array. + # %w[ a b c ] + # ^^^^ + # @param [RuboCop::AST::ArrayNode] node + # @return [String] + def whitespace_trailing(node) + node.source[node.children[-1].location.expression.end_pos...node.location.end.begin_pos] + end end end end diff --git a/lib/rubocop/cop/style/symbol_array.rb b/lib/rubocop/cop/style/symbol_array.rb index 3d57985306d..5926a17b72f 100644 --- a/lib/rubocop/cop/style/symbol_array.rb +++ b/lib/rubocop/cop/style/symbol_array.rb @@ -39,7 +39,7 @@ class SymbolArray < Base minimum_target_ruby_version 2.0 PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.' - ARRAY_MSG = 'Use `%s` for an array of symbols.' + ARRAY_MSG = 'Use %s for an array of symbols.' class << self attr_accessor :largest_brackets @@ -74,8 +74,7 @@ def build_bracketed_array(node) to_symbol_literal(c.value.to_s) end end - - "[#{syms.join(', ')}]" + build_bracketed_array_with_appropriate_whitespace(elements: syms, node: node) end def to_symbol_literal(string) diff --git a/lib/rubocop/cop/style/word_array.rb b/lib/rubocop/cop/style/word_array.rb index 2ae468ce946..3ed3863de93 100644 --- a/lib/rubocop/cop/style/word_array.rb +++ b/lib/rubocop/cop/style/word_array.rb @@ -44,7 +44,7 @@ class WordArray < Base extend AutoCorrector PERCENT_MSG = 'Use `%w` or `%W` for an array of words.' - ARRAY_MSG = 'Use `%s` for an array of words.' + ARRAY_MSG = 'Use %s for an array of words.' class << self attr_accessor :largest_brackets @@ -92,8 +92,7 @@ def build_bracketed_array(node) to_string_literal(word.children[0]) end end - - "[#{words.join(', ')}]" + build_bracketed_array_with_appropriate_whitespace(elements: words, node: node) end end end diff --git a/spec/rubocop/cop/style/symbol_array_spec.rb b/spec/rubocop/cop/style/symbol_array_spec.rb index d82891a4d3e..ddd4b4d11aa 100644 --- a/spec/rubocop/cop/style/symbol_array_spec.rb +++ b/spec/rubocop/cop/style/symbol_array_spec.rb @@ -220,6 +220,25 @@ RUBY end + it 'autocorrects multiline %i array' do + expect_offense(<<~RUBY) + %i( + ^^^ Use an array literal `[...]` for an array of symbols. + one + two + three + ) + RUBY + + expect_correction(<<~RUBY) + [ + :one, + :two, + :three + ] + RUBY + end + it 'autocorrects an array has interpolations' do expect_offense(<<~'RUBY') %I(#{foo} #{foo}bar foo#{bar} foo) diff --git a/spec/rubocop/cop/style/word_array_spec.rb b/spec/rubocop/cop/style/word_array_spec.rb index 2fc388aa09e..28a4f862145 100644 --- a/spec/rubocop/cop/style/word_array_spec.rb +++ b/spec/rubocop/cop/style/word_array_spec.rb @@ -416,6 +416,23 @@ RUBY end + it 'autocorrects multiline %w() array' do + expect_offense(<<~'RUBY') + %w( + ^^^ Use an array literal `[...]` for an array of words. + foo + bar + ) + RUBY + + expect_correction(<<~'RUBY') + [ + 'foo', + 'bar' + ] + RUBY + end + it 'autocorrects a %W() array which uses string with hyphen' do expect_offense(<<~'RUBY') %W(foo bar #{foo}-bar)