[Templates] PROPOSAL: Add COMMENT BLOCK directive.

Andy Wardley abw@wardley.org
Fri, 15 Jun 2007 13:06:36 +0100


Paul Seamons wrote:
> It would be nice for TT to support [the COMMENT] directive.

Yes, totally agree.  It's always been in the back of my mind for TT3, although 
  I hadn't really thought it through until now.

There's a number of issues here.  The first is that we definitely do want some 
kind of directive to indicate a block of content that shouldn't be processed. 
    So that's a definite "yes" from me.

The second issue is what we call it.  I think I prefer something more like 
SKIP than COMMENT.  First, it's shorter to type.  Second, and perhaps more 
importantly, I think it avoids the association that COMMENT has with "some 
text that is ignored by the parser".  In this case, the block must contain 
valid, parse-able and well structured content that is subsequently ignored. 
So although it's relatively easy to explain that COMMENT must include valid 
code, I think SKIP says it better without us having to explain much at all.

The third issue is that it's still rather clumsy to have to add:

   [% SKIP %]
      ...
   [% END %]

in order to block out some code temporarily.  There's little to be gained over 
the current work-around:

   [% IF 0 %]
      ...
   [% END %]

So I'm also in favour of having a more succinct block-level comment syntax.

It would be nice if Robert's suggestion worked:

 > [%#
 >      Template section that won't be shown
 >      [% var_a %]
 >      [% IF foo %]Foo[% END %]
 > %]

Unfortunately, it doesn't, and I don't think it can be made to work without 
some serious effort.  And then, it's almost certain to Do The Wrong Thing from 
time to time.

   [%# we could just about make this work if we counted and matched [% and %]
       x = [% BLAH %]
   %]
   ^^ ends here


   [%# but we can only make this work if we parse the (unparseable) comment
     # to work out which bits are strings and which bits aren't
       x = '[% BLAH' %]
   %]                ^^ does it end here?
   ^^ or here?

Something like this is more like it:

 > [%#*
 >
 >   This is a block comment - notice the matching opening and closing comment
 > tags.
 >   [% foo
 >
 > *#%]

As Paul says:
 > Using the [%#* Way is easy to check for and would allow a fast parse to the
 > closing tag.

And that's the key to it.  You want something that is totally distinct from 
regular TT tags so the parser can safely know when the comment ends.  However, 
that particular syntax is starting to look a bit of a handful to type (or 
remember!)

Anyway, there's already a fairly simple solution to this in TT3, or at least 
the mechanism is there to implement it.  The template scanner is able to 
recognise (theoretically) any number of different tags embedded in a document.

Unlike TT2 which first looks for [% %] tags and then goes back to interpolate 
$foo and ${foo} in the remaining text blocks if the INTERPOLATE option is set, 
  TT3 matches the different tags types (and any others you want to throw in) 
in one pass.

The basic tags are these:
   [% %]
   ${foo}        # only enable when INTERPOLATE set
   $foo          # ditto

Then there's also a magical '\' escape tag which is activated when the 
INTERPOLATE flag is set.  This is used to 'protect' any '$' signs that you 
don't want interpolated

   abc cost $price  # a variable
   xyz cost \$100   # not a variable

With this mechanism in place, we can easily define an extra tag style to match 
#* ... *# or something similar.  I was originally considering /% ... %/ which 
is closer to C-style block comments, with a hint of TT-ness added.

Like this:

   /% this is a block comment
      [% INCLUDE foo %]
      blah blah blah
      [% GET x IF y ON tuesday %]
   %/

   /% [% INCLUDE blah %] %/

Hmmm... let's compare it to this:

   #* this is a block comment
      [% INCLUDE foo %]
      blah blah blah
      [% GET x IF y ON tuesday %]
   *#

   #* [% INCLUDE blah %] *#

With hindsight, there's a bit of percentage overload in that first example, 
and the second does stand out more visually.  There may also be other 
possibilities worth considering, but whatever we eventually decide on can be 
easily changed by the end user.

Adding tags will be done something like this:

     my $tt = Template->new(
	tags = [
             comment => { start => '#*', end => '*#' }
         ],
     );

In the background, that'll load and instantiate a Template::Tag::Comment 
object with the specified start and end tags, and hook it into the scanner. 
The T::T::Comment scan() method will simply scan to the end tag and ignore the 
content.

I'm sure with a sprinkling of syntactic sugar, we'll be able to get that down 
to something like:

     my $tt = Template->new( comment => '#* *#' );

We can also provide a directive that allows you to define a comment style 
within the document.  COMMENT or COMMENTS would be ambiguous, I think, for the 
same reasons mentioned above.  Maybe IGNORE?

     [% IGNORE #* *# %]

In which case we might want to rename T::Tag::Comment as T::Tag::Ignore and 
use the 'ignore' option instead, to keep things consistent.

     my $tt = Template->new( ignore => '#* *#' );

So to summarise, my official ruling is "yes" to all of the above, while 
reserving the right to change names to protect the innocent.  :-)

Cheers
A