Writing a multiline string with trailing whitespace for exact formatting must not cause a Style/TrailingWhitespace offense because rubocop cannot know the intended use of a string.
Rubocop marks strings with trailing whitespace as a Style/TrailingWhitespace offense
Write t.rb:
STRING = <<-STRING
This line has a trailing space:
This line has a trailing tab:
Nothing in this file should cause a Style/TrailingWhitespace offense.
STRING
run rubocop:
$ rubocop t.rb
Inspecting 1 file
C
Offenses:
t.rb:1:10: C: Freeze mutable objects assigned to constants.
STRING = <<-STRING
^^^^^^^^^
t.rb:2:1: C: Use 2 spaces for indentation in a heredoc by using some library(e.g. ActiveSupport's String#strip_heredoc).
This line has a trailing space: ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:2:32: C: Trailing whitespace detected.
This line has a trailing space:
^
t.rb:4:30: C: Trailing whitespace detected.
This line has a trailing tab:
^
1 file inspected, 4 offenses detected
I do not expect the cops on lines 2 or 4 to appear.
Include the output of rubocop -V. Here's an example:
$ rubocop -V
0.49.1 (using Parser 2.4.0.0, running on ruby 2.4.1 x86_64-darwin16)
Guys, I've looked into implementation of this cop. Wanted to fix this bug, but I have couple questions.
Current implementation looks like this:
def investigate(processed_source)
processed_source.lines.each_with_index do |line, index|
next unless line.end_with?(' ', "\t")
# couple more lines
end
end
So it goes through each line of code. Each line of code is a string.
To fix this bug I need to work with line of code as with node, right?
I found mixin called Heredoc which provides method on_heredoc. I could use that one.
Do you have any suggestions on proper implementation for this case?
Btw, for this cop there is an example that says: _it registers an offense for trailing whitespace in a heredoc string_ , so it was expected behavior for author of the cop. :)
@smakagon: The high level strategy I would use might be:
it was expected behavior for author of the cop
Yes. The suggested behaviour needs a configuration option. 馃檪
@Drenmi thanks for the answer.
To store all heredoc line ranges, I need to figure out that it's a heredoc.
It's not possible with current way of processing with: investigate(processed_source).
I was struggling with that because from one side I need to work with code as with an array of strings, but from the other side I want to know if it's a heredoc.
The investigate method is called before the on_ methods. If it was the other way around we'd be laughing. Now it's a bit tricky to figure out how we can first store all the line numbers where there are heredocs, and then check all lines. Or did you have a solution in mind for this @Drenmi?
@jonas054: Hm. That definitely makes it less straightforward than I envisioned. 馃
@Drenmi I played around a little and tried the following:
diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb
index a662b5b..5d70a74 100644
--- a/lib/rubocop/cop/layout/trailing_whitespace.rb
+++ b/lib/rubocop/cop/layout/trailing_whitespace.rb
@@ -5,6 +5,7 @@ module RuboCop
module Layout
# This cop looks for trailing whitespace in the source code.
class TrailingWhitespace < Cop
+ include Heredoc
MSG = 'Trailing whitespace detected.'.freeze
def investigate(processed_source)
@@ -19,6 +20,11 @@ module RuboCop
end
end
+ def on_heredoc(node)
+ heredoc_body = node.loc.heredoc_body
+ offenses.reject! { |o| heredoc_body.overlaps?(o.location) }
+ end
+
def autocorrect(range)
->(corrector) { corrector.remove(range) }
end
This is perhaps a bit ugly. We haven't used offenses.reject! in any cops before, removing already added offenses. But it does get the job done.
RubuCop still seems to want to treat heredoc strings as code, at least in some cops:
~ruby
puts <<~MSG
\nSome message: #{mystuff}
This is bad!
MSG
~


This is in RubyMine 2017.3.2 with RubuCop 0.52.1.
@reitzig Squiggly heredocs (~MSG) were introduced in Ruby 2.3. That's why the parser gem has problems with the syntax.
Add this to your configuration:
AllCops:
TargetRubyVersion: 2.3
That makes sense. I'll have to see how to configure this for the "built-in" RuboCop in RubyMine. Thanks!
For reference: there are issues on the RubyMine bugtracker about this, e.g. this.
_Amendment:_ Adding a .rubocop.yml to the project root was enough.