Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format: keep trailing spaces in macros #7099

Conversation

makenowjust
Copy link
Contributor

Currently the formatter removes trailing spaces in macros, however macro may contain meaningful trailing spaces (e.g. space inside string literals), so it is possible to change program meaning.
The formatter should keep such spaces because formatter doesn't change input program meaning at any time.

@straight-shoota
Copy link
Member

Could you give an example where removing a trailing whitespace changes semantics?

@makenowjust
Copy link
Contributor Author

@straight-shoota Just " string literal value ".

Rewriting string literal value by formatter is totally evil and unintentionally.

When the following code is given:

(NOTE: Please drag this code. Heredoc LEFT_ALIGNED lines have trailing spaces.)

left_aligned_string = <<-LEFT_ALINGED
  1        
  2        
  Fizz     
  4        
  Buzz     
  Fizz     
  7        
  8        
  Fizz     
  Buzz     
  11       
  Fizz     
  13       
  14       
  FizzBuzz 
  LEFT_ALINGED

puts left_aligned_string.lines.map { |line| line + line }.join "\n"

Var left_aligned_string lines are lieft-aligned, so the output is nice.

$ crystal run left_aligned.cr
1        1        
2        2        
Fizz     Fizz     
4        4        
Buzz     Buzz     
Fizz     Fizz     
7        7        
8        8        
Fizz     Fizz     
Buzz     Buzz     
11       11       
Fizz     Fizz     
13       13       
14       14       
FizzBuzz FizzBuzz 

Encloses this code by macro and adds this invocation, then formats the file by current crystal tool format. After, the file is that:

macro main
  left_aligned_string = <<-LEFT_ALINGED
    1
    2
    Fizz
    4
    Buzz
    Fizz
    7
    8
    Fizz
    Buzz
    11
    Fizz
    13
    14
    FizzBuzz
    LEFT_ALINGED

  puts left_aligned_string.lines.map { |line| line + line }.join "\n"
end

main

Trailing spaces inside heredocs are removed, so the output is broken.

$ crystal tool format -< fizz.cr | crystal eval
11
22
FizzFizz
44
BuzzBuzz
FizzFizz
77
88
FizzFizz
BuzzBuzz
1111
FizzFizz
1313
1414
FizzBuzzFizzBuzz

Yes, it is very conservative fix. I believe the formatter should not break the given source code of course. The output of crystal tool format foo.cr; crystal run foo.cr should equal the output of crystal run foo.cr.

@asterite
Copy link
Member

Even though it's a conservative change, having invisible code is a really bad practice in my opinion.

@straight-shoota
Copy link
Member

In your example the trailing whitespace is removed inside a string literal (heredoc). That's obviously incorrect, whether it's inside a macro method or not.

But as far as I can tell, this PR is not limited to string literals. Can you show an example where in a macro trailing whitespace is relevant outside a string literal?
Please excuse me if I didn't catch the purpose correctly.

@asterite
Copy link
Member

@straight-shoota That's the only use case, string literals inside macros. If I remember correctly the macro processor doesn't know it's inside a string literal, everything is just "text".

@straight-shoota
Copy link
Member

@asterite If I understand correctly, a part of the issue is that trailing whitespace in a string literal is handled differently inside a macro method. ( signals the presence of trailing whitespace)

# formatter preserves trailing whitespace
<<-EOF
foo  ¶
EOF

macro foo
  # formatter removes trailing whitespace
  <<-EOF
  foo  ¶
  EOF
end

The formatter should treat both cases the same, either remove or preserve trailing whitespace.

@makenowjust
Copy link
Contributor Author

makenowjust commented Nov 21, 2018

@straight-shoota Backslash line continuation is another example.

When it has trailing spaces, it cannot compile. Otherwise, it pass through the compiler if the formatter removes trailing spaces.

macro foo
  foo = 1
  p foo \  
    + 1
end

foo

@asterite You are right and I think so too. However I also think it is linter field.

I'm surveying other formatters and linters (with no-trailing-spaces option) status. I'll submit detailed report later, but there is no formatter removes trailing spaces inside string literal in silence for now.

@asterite
Copy link
Member

In any case I think this should be fixed. I think the formatter does a last pass that removes trailing whitespace so this migh be a bit hard to implement, but this PR seems to work, so 👍 from me (I didn't have time to see the diff yet).

Copy link
Contributor

@RX14 RX14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that the formatter cannot format macros, but the formatter should not ever break code. That's it's primary directive and breaking it makes developers loose confidence in autoformat.

@RX14 RX14 added this to the 0.27.1 milestone Nov 27, 2018
@RX14 RX14 added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler topic:tools:formatter topic:tools labels Nov 27, 2018
@@ -1234,4 +1234,31 @@ describe Crystal::Formatter do
# 3 + 4
# ```
CODE

# Keep trailing spaces in macros.
Copy link
Member

@bcardiff bcardiff Dec 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Editors are usually configured to trim white spaces at the end.
These specs should be written different in order to avoid loosing those spaces.

makenowjust added a commit to makenowjust/crystal that referenced this pull request Dec 12, 2018
@makenowjust
Copy link
Contributor Author

Perhaps CI will fail because I found a new formatter bug. I think it is irrelevant to this PR, so I will open a new PR when I fixed this.

@makenowjust
Copy link
Contributor Author

done

@bcardiff
Copy link
Member

I was expecting a single line string filled with " \n \n " to preserve trailing spaces 😄. The proposed way is far more clear.

Feel free to change it to " \n \n " if you timeout with the formatter bug you reached.

@makenowjust
Copy link
Contributor Author

@bcardiff However the first spec of this is too long to write as single line, so I'd write to:

assert_format(
  "macro foo\n" +
  "  <<-FOO\n" +
  "    hello  \n" +
  "  FOO\n" +
  "end\n" +
  "{% verbatim do %}\n" +
  "  <<-FOO\n" +
  "    hello  \n" +
  "  FOO\n" +
  "{% end %}\n" +
  "{% if true %}\n" +
  "  <<-FOO\n" +
  "    hello  \n" +
  "  FOO\n" +
  "{% end %}\n" +
  "{% for a in %w() %}\n" +
  "  <<-FOO\n" +
  "    hello  \n" +
  "  FOO\n" +
  "{% end %}"
)

What do you think?

@straight-shoota
Copy link
Member

@makenowjust You could put the individual samples each on a single line string. And maybe use line continuation (\) instead of concatenating strings (+)?

Copy link
Member

@sdogruyol sdogruyol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @makenowjust 👍

@RX14
Copy link
Contributor

RX14 commented Dec 15, 2018

needs rebase

Currently the formatter removes trailing spaces in macros, however macro
may contain meaningful trailing spaces (e.g. space inside string literals),
so it is possible to change program meaning.
The formatter should keep such spaces because formatter doesn't change
input program meaning at any time.
But keep trailing spaces in macro body outside macro expressions
@makenowjust makenowjust force-pushed the fix/format-keep-trailing-space-in-macro branch from 63f7a27 to f52b90b Compare December 16, 2018 22:41
@makenowjust
Copy link
Contributor Author

rebased

@asterite asterite merged commit 79becb2 into crystal-lang:master Dec 16, 2018
@makenowjust makenowjust deleted the fix/format-keep-trailing-space-in-macro branch December 17, 2018 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:tools:formatter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants