From fa5ad0fa3ee10c7bf09c938aeb0ac7f502dba0eb Mon Sep 17 00:00:00 2001 From: Jonas Arvidsson Date: Fri, 23 Jul 2021 20:48:57 +0200 Subject: [PATCH] [Fix #9945] Fix heredoc indentation in trailing space corrections Whitespace-only lines within heredocs that are shorter than the indentation level, or equal to it, should be removed when correcting trailing whitespace. Inserting string interpolation tokens on these lines changes the indentation level for squiggly heredocs. --- changelog/fix_heredoc_trailing_spaces.md | 1 + lib/rubocop/cop/layout/heredoc_indentation.rb | 7 --- lib/rubocop/cop/layout/trailing_whitespace.rb | 25 ++++++++++- lib/rubocop/cop/mixin/heredoc.rb | 7 +++ .../cop/layout/trailing_whitespace_spec.rb | 43 +++++++++++++++++++ 5 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 changelog/fix_heredoc_trailing_spaces.md diff --git a/changelog/fix_heredoc_trailing_spaces.md b/changelog/fix_heredoc_trailing_spaces.md new file mode 100644 index 00000000000..f73db3ece64 --- /dev/null +++ b/changelog/fix_heredoc_trailing_spaces.md @@ -0,0 +1 @@ +* [#9945](https://github.com/rubocop/rubocop/issues/9945): Fix auto-correction of lines in heredocs with only spaces in `Layout/TrailingWhitespace`. ([@jonas054][]) diff --git a/lib/rubocop/cop/layout/heredoc_indentation.rb b/lib/rubocop/cop/layout/heredoc_indentation.rb index c545a5ea443..6054580c6f7 100644 --- a/lib/rubocop/cop/layout/heredoc_indentation.rb +++ b/lib/rubocop/cop/layout/heredoc_indentation.rb @@ -143,13 +143,6 @@ def base_indent_level(node) indent_level(base_line) end - def indent_level(str) - indentations = str.lines - .map { |line| line[/^\s*/] } - .reject { |line| line.end_with?("\n") } - indentations.empty? ? 0 : indentations.min_by(&:size).size - end - # Returns '~', '-' or nil def heredoc_indent_type(node) node.source[/^<<([~-])/, 1] diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb index 40337030676..751bfa1e84b 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -41,6 +41,7 @@ module Layout # class TrailingWhitespace < Base include RangeHelp + include Heredoc extend AutoCorrector MSG = 'Trailing whitespace detected.' @@ -54,6 +55,8 @@ def on_new_investigation end end + def on_heredoc(_node); end + private def process_line(line, lineno) @@ -63,13 +66,33 @@ def process_line(line, lineno) range = offense_range(lineno, line) add_offense(range) do |corrector| if heredoc - corrector.wrap(range, "\#{'", "'}") unless static?(heredoc) + process_line_in_heredoc(corrector, range, heredoc) else corrector.remove(range) end end end + def process_line_in_heredoc(corrector, range, heredoc) + indent_level = indent_level(find_heredoc(range.line).loc.heredoc_body.source) + whitespace_only = whitespace_only?(range) + if whitespace_only && whitespace_is_indentation?(range, indent_level) + corrector.remove(range) + elsif !static?(heredoc) + range = range_between(range.begin_pos + indent_level, range.end_pos) if whitespace_only + corrector.wrap(range, "\#{'", "'}") + end + end + + def whitespace_is_indentation?(range, level) + range.source[/ +/].length <= level + end + + def whitespace_only?(range) + source = range_with_surrounding_space(range: range).source + source.start_with?("\n") && source.end_with?("\n") + end + def static?(heredoc) heredoc.loc.expression.source.end_with? "'" end diff --git a/lib/rubocop/cop/mixin/heredoc.rb b/lib/rubocop/cop/mixin/heredoc.rb index 8cd8aa06a25..75f67e1a799 100644 --- a/lib/rubocop/cop/mixin/heredoc.rb +++ b/lib/rubocop/cop/mixin/heredoc.rb @@ -20,6 +20,13 @@ def on_heredoc(_node) private + def indent_level(str) + indentations = str.lines + .map { |line| line[/^\s*/] } + .reject { |line| line.end_with?("\n") } + indentations.empty? ? 0 : indentations.min_by(&:size).size + end + def delimiter_string(node) node.source.match(OPENING_DELIMITER).captures[1] end diff --git a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb index ca9fde04716..46ffe48eaef 100644 --- a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb +++ b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb @@ -144,6 +144,49 @@ RUBY end + it 'corrects by removing trailing whitespace used for indentation in a heredoc string' do + expect_offense(<<~RUBY) + x = <<~EXAMPLE + no trailing + #{trailing_whitespace} + ^^ Trailing whitespace detected. + no trailing + #{trailing_whitespace} + ^ Trailing whitespace detected. + no trailing + EXAMPLE + RUBY + + expect_correction(<<~RUBY) + x = <<~EXAMPLE + no trailing + + no trailing + + no trailing + EXAMPLE + RUBY + end + + it 'corrects a whitespace line in a heredoc string that is longer than the indentation' do + expect_offense(<<~RUBY) + x = <<~EXAMPLE + no trailing + #{trailing_whitespace} + ^^^ Trailing whitespace detected. + no trailing + EXAMPLE + RUBY + + expect_correction(<<~RUBY) + x = <<~EXAMPLE + no trailing + \#{' '} + no trailing + EXAMPLE + RUBY + end + it 'does not correct trailing whitespace in a static heredoc string' do expect_offense(<<~RUBY) x = <<~'EXAMPLE'