Haml Whitespace-Handling Sucks, Too

Posted April 30, 2008

I want to thank everyone who replied to my last post and told me everything that was wrong with Haml error messages. I think I’ve fixed everything that was mentioned there, but if something else comes up please let me know.

I was surprised to learn that most of the problems were with line number inaccuracies and the error not managing to bubble up through Rails properly. I was going to do a similar post for Sass, but since it doesn’t seem to have the same issues, I think I’ll skip it.

Instead, I thought we could do some brainstorming. Haml’s been missing an important bit of functionality for a while now. Although it’s powerful enough to express almost everything you can with plain XHTML, Haml isn’t good at not producing whitespace.

Most of the time, this doesn’t matter at all. Whitespace tends to be meaningless in HTML in general, and Haml’s pretty good about handling explicitly whitespace-sensitive elements like pre and textarea[1].

Unfortunately, there are a few cases where whitespace does matter. This usually comes up when you want two elements to be next to each other without any whitespace in between. And Haml has a really hard time with this.

This needs to be fixed, of course. The issue is coming up with a single addition to the language that will fix as many cases as possible, while also being clean and simple. Several suggestions have been floated, but I don’t think I’ve seen one that works for everything it needs to.

The Problems

There are three main cases where whitespace is undesirable. Here they are in terms of XHTML examples that we’d like to generate.

  1. <img /><img /><img />

    Putting any whitespace tags between images stops them from lining up exactly, which is necessary for some layouts. The same effect may be achievable using CSS, but even so it really should be done in markup.

    The best way to do this currently is to generate the images using pure Ruby, something like:

    stuff.map { |s| image_tag ... }.join
    But that’s kind of kludgey.
  2. <pre>foo
    bar</pre>

    There are various cases where you don’t want any whitespace between the opening and closing tags of an element and the first and last bits of content. One of these, whitespace-sensitive tags, is shown above.

    The best way to do this right now would be to create some sort of post-processing helper. No such helper exists at the moment, though, largely because a language-integrated solution would be better.
  3. I like <a href="http://franschocolates.com">chocolate</a>!

    Often when using links and other sorts of inline formatting you want to format a word but not the punctuation immediately after the word. Naturally, a space between the word and the punctuation isn’t good.

    Haml is really designed for structural markup; thus, it purposefully leaves formatting markup to languages that are designed for it. One such language is XHTML; so one solution for this is to include the raw XHTML in the document. Another solution would be to use the Textile or Markdown filter:

    :markdown
      I like [chocolate](http://franschocolates.com)!
    This isn’t a terrible solution. But if the solution for the other whitespace issues solve this one as well, the more is for its merit.

The Solution

There is no solution. Yet. That’s what this post is for: I’d like all of you folks to wrack your brains and see if you can come up with some way to make these constructs possible. If you have an idea, post it here; if we get one that’s workable, it’ll go into the soon-to-be-released Haml 2.0.

1 In the master branch, you don’t even need to use ~ most of the time.

Evgeny said May 01, 2008:

One solution that you can use in (X)HTML is to insert whitespace into the elements themselves, since that does not matter for markup. So perhaps we could somehow make HAML do this automagically, or with an instruction or somthing.

What I mean looks like this:

<img
/><img
/><img
/>

Now each image tag is on it’s own line (like haml would do it), but there is no whitespace.

In HAML it could be something like %~img.

The case of <div> shortcuts:

  • %~ is an empty <div>, and is okay since there are no tags in xml that can start with ~, like <~img> ...
  • ~#id for “auto-div-from-id” like #id
  • ~.class for “auto-div-from-class”

Also there could be a global option that will ALWAYS do this in all the HAML everywhere, so you just flip the switch and get rid of ALL the whitespace between tags. Like the ugly mode…

It can also stick the opening < to the preceeding element, like:

HAML:

%img/
%~img/
%img/

HTML:

<img /><
img
/><img />
Evgeny said May 01, 2008:

Here is a carefully crafted example of how I see it in action: http://pastie.caboo.se/189826

Nathan said May 01, 2008:

It seems like this only solves problem 1, and I’m not too keen on the HTML output. Shoving whitespace into tags themselves seems uglier than just putting them all on the same line.

RyanTM said May 01, 2008:

For number 3 here’s what I do right now:

==I like #{link_to "chocolate", "http://franschocolates.com"}!

It like this approach but it took some discovering, it’s wasn’t obvious from the introductory tutorials.

Sunny said May 03, 2008:

How about a \ at the end of the line ? It would mean “please, don’t put whitespace right after this”.

%img \
%img \
%img

%pre \
  foo
  bar \

I like
%a{ :href => chocolate_path } \
!
Nathan said May 03, 2008:

I like the idea, but wouldn’t

%pre \
  foo
  bar \
baz

be saying “don’t put whitespace after the pre”? So it would produce

<pre>
  foo
  bar</pre>baz

Maybe it could be combined with the following syntax (which was, if memory serves, suggested a while ago by Steve Ross):

%pre)
  foo
  bar

The ) here means “please don’t put whitespace inside this.”

Sunny said May 04, 2008:

Hmmm, indeed. Perhaps the “)” could still be a ”\” if we consider that having any content underneath the tag means removing the whitespace inside the tag rather than after.

%pre foo bar \
baz

%pre \
  foo
  bar
baz

%pre \
  foo
  bar \

Would produce :

<pre>foo bar</pre>baz

<pre>foo
  bar</pre>
baz

<pre>foo
  bar</pre>baz

(Thank you Textile for coping with all the <pre>s I’m throwing at you ;).)

Richard Davies said May 29, 2008:

I maintain GHRML, which is a clone of HAML for Python (Genshi, Django, etc.). We implement a whitespace-eating tag, %nospace (could equally be %\, I guess). For example, we would write:

I like
%a{'href':'http://franschocolates.com'}chocolate
%nospace
!
Vitaly Kushner said May 14, 2009:

The solution is very simple. try the following:

%img<
%img<
%img

it will produce this html:

<img><img><img>
Matchu said August 21, 2009:

Vitaly: I’m pretty sure this post was written before the whitespace-removal syntax was added ;)

Anonymous said October 23, 2009:

Is this solved now? If it is, I haven’t figured it out for example 3 :) Using > or < gives me “I like chocolate !” or “I likechocolate!” not “I like chocolate!”

Nathan said October 24, 2009:

Example 3 is one case not covered well by the whitespace chompers. As I mentioned in the blog post, Haml’s designed for structural markup, not textual styling. So your best bet is still using markdown or inline tags.

Mark Coleman said October 24, 2009:

I dunno about structural markup vs textual styling… I’ve always thought of haml as ‘a neat replacement to erb’. As such, its pretty great at structure and textual styling and does everything erb does, better. Except this one edge case.

Here is another example . I don’t know if you call that textual styling, or texual structure… If the chompers worked without a tag, problem solved, I think.

Anyway, haml is still great! Thanks! :)

Nathan said October 25, 2009:

Haml’s certainly not any worse than ERB at handling textual styling. Here’s how I might do your example: http://gist.github.com/217976

Trevor said May 27, 2010:

Here we over two years later and white space handling still sucks :(

Shaun Chapman said August 04, 2010:

I’ve been using the following in Rails 3:

=raw "#{link_to 'Example', 'http://example.com/'}."

It’s definitely not a perfect solution, but it’s more readable than anything else that I’ve come up with.

HAML should support directional whitespace removal. For example:

This is an example
= link_to 'example', 'http://example.com/' >>
.

...would produce:

This is an <a href="http://example.com/">example</a>.

...and:

This is an example
= link_to 'example', 'http://example.com/'
<< .

...would produce the same.

Nathan said August 08, 2010:

Shaun: The problem with such fine-grained whitespace removal is that it adds significant amounts of syntax that then has to be learned in order to be used. And even then, it would probably still leave some holes that aren’t covered.

For the curious, the current thinking on whitespace-handling is well-explained by this post. The gist is that the Haml syntax simply isn’t well-suited for this, and that you should drop into plain HTML and/or Haml’s built-in string interpolation when you need complex inline formatting.

Make your comments snazzy with Textile!