crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.24k stars 1.61k forks source link

Formatter doesn't deindent HEREDOC literals #11066

Open Blacksmoke16 opened 2 years ago

Blacksmoke16 commented 2 years ago
require "spec"

describe "top" do
  it "third" do
    output.should eq(<<-XML
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
        <foo>x</foo>
        <foo/>
      </root>\n
      XML
    )
  end
end

If you were to remove the it block it gets formatted to:

require "spec"

describe "top" do
  output.should eq(<<-XML
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
        <foo>x</foo>
        <foo/>
      </root>\n
      XML
  )
end

I would have expected the HEREDOC body to also be de-indented by 2 spaces.

straight-shoota commented 2 years ago

I think the expected behaviour is what it should be. It's not hard to fix (I already have a patch that even simplifies the entire fix_heredocs method).

An obvious follow-up question: what if it's the other way around and indent increases?

begin
foo <<-FOO
  bar
  FOO
end

Most would probably expect that to become:

begin
  foo <<-FOO
    bar
    FOO
end

We can do that and keep the relative difference between the indents of the start and end delimiter lines. This would only be limited by the amount of available leading whitespace. If a negative difference is larger than the end delimiter's indent, the here doc can only be shifted left to column 1.

Maybe the heredoc's relative base indent should also be adjusted by the formatter, but that's a separate discussion (#11070).

straight-shoota commented 2 years ago

However, there is also the question of what the heredoc indent is relative to.

# What is the heredoc body relative to?
foo  <<-FOO
  bar
  FOO
# indent of the start delimiter line
foo <<-FOO
  bar
  FOO
# column of the start delimiter
foo <<-FOO
 bar
 FOO
straight-shoota commented 2 years ago

fix_heredocs is entirely broken for consecutive heredoc literals which all start on the same line. The formatter assumes the start delimiter is in lines[fix.start_line - 1] which is not true in cases like this:

foo <<-FOO, <<-BAR
   foo
  FOO
   bar
  BAR