Feature #9098

Indent heredoc against the left margin by default when "indented closing identifier" is turned on.

Added by Prem Sichanugrist 5 months ago. Updated 5 months ago.

[ruby-core:58243]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:syntax
Target version:next minor

Description

tl;dr: I would like to port +String#stripheredoc+ (http://api.rubyonrails.org/classes/String.html#method-i-stripheredoc) from Rails and enable it on <<- heredoc.

Hi,

I've been using here document (heredoc) for a while, and I've found out that it's very annoying that the content is always treated as flushed left. Per syntax doc:

Note that the while the closing identifier may be indented, the content is
always treated as if it is flush left.  If you indent the content those spaces
will appear in the output.

So, this is the current result if you use heredoc in Ruby:

class FancyHello
  def self.hello
    puts <<-README.inspect
      Hello
        World!
    README
  end
end

FancyHello.hello # => "      Hello\n        World!\n"

In Rails, the core team has implemented +String#strip_heredoc+, which handles this scenario by remove leading white spaces from the string. However, not many user know that Rails has that method, and instead doing this to get around it:

class FancyHello
  def self.hello
    puts <<-README.inspect
Hello
  World!
    README
  end
end

FancyHello.hello # => "Hello\n  World!\n"

I really think that we could do better on this by removing the leading white spaces, matching +String#strip_heredoc+ method.

So, after the change, I want to be able to do this, and get this result:

class FancyHello
  def self.hello
    puts <<-README.inspect
      Hello
        World!
    README
  end
end

FancyHello.hello # => "Hello\n  World!\n"

Note: the behavior on <<HEREDOC will stay the same, as I feel like in that case you really want the input to be flushed left.

I'll write up a patch and submit it if you think this is a good idea. Please let me know any question or concern you may have.

Thank you,

Prem

History

#1 Updated by Eric Hodel 5 months ago

  • Status changed from Open to Assigned
  • Assignee changed from Prem Sichanugrist to Yukihiro Matsumoto
  • Target version set to Next Major

Why not make your content flush left?

#2 Updated by Prem Sichanugrist 5 months ago

@drbrain I could, but then the code would look ugly when the non-heredoc section is well-indented, and especially if the heredoc is pretty long. Consider this example code:

module Rails
  class CommandsTasks # :nodoc:
    attr_reader :argv

    HELP_MESSAGE = <<-EOT
Usage: rails COMMAND [ARGS]

The most common rails commands are:
 generate    Generate new code (short-cut alias: "g")
 console     Start the Rails console (short-cut alias: "c")
 server      Start the Rails server (short-cut alias: "s")
 dbconsole   Start a console for the database specified in config/database.yml
             (short-cut alias: "db")
 new         Create a new Rails application. "rails new my_app" creates a
             new application called MyApp in "./my_app"

In addition to those, there are:
 application  Generate the Rails application code
 destroy      Undo code generated with "generate" (short-cut alias: "d")
 plugin new   Generates skeleton for developing a Rails plugin
 runner       Run a piece of code in the application environment (short-cut alias: "r")

All commands can be run with -h (or --help) for more information.
EOT

    COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)

Compared to this:

module Rails
  class CommandsTasks # :nodoc:
    attr_reader :argv

    HELP_MESSAGE = <<-EOT
      Usage: rails COMMAND [ARGS]

      The most common rails commands are:
       generate    Generate new code (short-cut alias: "g")
       console     Start the Rails console (short-cut alias: "c")
       server      Start the Rails server (short-cut alias: "s")
       dbconsole   Start a console for the database specified in config/database.yml
                   (short-cut alias: "db")
       new         Create a new Rails application. "rails new my_app" creates a
                   new application called MyApp in "./my_app"

      In addition to those, there are:
       application  Generate the Rails application code
       destroy      Undo code generated with "generate" (short-cut alias: "d")
       plugin new   Generates skeleton for developing a Rails plugin
       runner       Run a piece of code in the application environment (short-cut alias: "r")

      All commands can be run with -h (or --help) for more information.
    EOT

    COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)

From the code example, you could see that it looks much nicer when it's properly indented based on the level. The heredoc doesn't feel foreign and stand out-of-place anymore.

#3 Updated by Eric Hodel 5 months ago

I can see the second is worse, not better, because it runs over 80 columns on more lines which wrap in my editor.

#4 Updated by Prem Sichanugrist 5 months ago

I can see that. I actually just indented the example in without looking at the line width. My bad. :)

If the second example is not over 80 columns, would that still be consider worse for you?

#5 Updated by Eric Hodel 5 months ago

I prefer Avdi's idea in as it does not introduce incompatibility.

#6 Updated by Avdi Grimm 5 months ago

On Sat, Nov 9, 2013 at 4:18 PM, sikachu (Prem Sichanugrist) s@sikac.hu
wrote:

I've been using here document (heredoc) for a while, and I've found out

that it's very annoying that the content is always treated as flushed left.

This has actually bugged me for a very long time. Other languages have
triple-quoted strings where the indent of the opening triple-quote is taken
into account, and automatically stripped from the resulting text. In Ruby
if I don’t want leading spaces I have to break the visual flow of my
indentation (or resort to something like #strip_heredoc).

I don’t agree with changing the existing heredocs to have new semantics.
But I could see adding e.g. a "squiggly-heredoc":

class Foo
BAR = <<~EOF
This line is
cleanly indented
EOFend
Foo::BAR # => "This line is\ncleanly indented"

Note that the way I imagine it, this would work a little different than
triple-quoted strings in other languages, since the indentation wouldn’t be
determined by the location of the opening signifier. Instead, it would be
determined by indentation of the first non-whitespace character inside the
heredoc.
--
Avdi Grimm
http://avdi.org

I only check email twice a day. to reach me sooner, go to
http://awayfind.com/avdi

#7 Updated by Prem Sichanugrist 5 months ago

Oh, that's an awesome suggestion, @avdi. I like it. That way, it wouldn't be introducing backward incompatibility. Maybe we can target next minor (since it's a new feature) as well?

@drbrain, so I think I'll want to change this ticket to be adding <<~ to do this functionality instead. Should I go ahead and try to implement it, or do I have to wait for @matz to +1?

#8 Updated by Eric Hodel 5 months ago

  • Target version changed from Next Major to next minor

Creating a patch will help, but it can't be committed without the approval of matz.

#9 Updated by Rodrigo Rosenfeld Rosas 5 months ago

+1 to Avdi's idea. I mostly miss this when I'm writing my specs. Sometimes I have to compare some processed HTML with the expected result and it's not easy to write the expected string without compromising indentation. Usually I end up with something like:

[
'First line',
'Second line',
...
].join "\n"

Having something like what Avdi described would help me a lot to write some of my specs.

#10 Updated by Prem Sichanugrist 5 months ago

Sweet. I'll go ahead and start cooking up the patch. Thanks all for the preliminary feedback.

Also available in: Atom PDF