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
!

Make your comments snazzy with Textile!