From eb225b0b482f3c22ff45ae42da2b9bed594d1b40 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 21 Oct 2010 23:19:10 +0000 Subject: [PATCH] Added skipping of newlines immediately following section open and close tags. --- .../com/samskivert/mustache/Mustache.java | 32 ++++++++++++++++--- .../com/samskivert/mustache/MustacheTest.java | 19 +++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java index 0bf3b7b..bafbfc9 100644 --- a/src/main/java/com/samskivert/mustache/Mustache.java +++ b/src/main/java/com/samskivert/mustache/Mustache.java @@ -75,6 +75,7 @@ public class Mustache int state = TEXT, startPos = 0, endPos = 0; StringBuilder text = new StringBuilder(); int line = 0; + boolean skipNewline = false; while (true) { char c; @@ -90,6 +91,13 @@ public class Mustache if (c == '\n') { line++; + // if we just parsed an open section or close section task, we'll skip the first + // newline character following it, if desired; TODO: handle CR, sigh + if (skipNewline) { + continue; + } + } else { + skipNewline = false; } switch (state) { @@ -126,6 +134,7 @@ public class Mustache // TODO: change delimiters } else { accum = accum.addTagSegment(text, line); + skipNewline = accum.skipNewline(); } state = TEXT; } else { @@ -142,6 +151,7 @@ public class Mustache // TODO: change delimiters } else { accum = accum.addTagSegment(text, line); + skipNewline = accum.skipNewline(); } state = TEXT; } else { @@ -195,6 +205,12 @@ public class Mustache _compiler = compiler; } + public boolean skipNewline () { + // return true if we just added a compound segment which means we're immediately + // following the close section tag + return (_segs.size() > 0 && _segs.get(_segs.size()-1) instanceof CompoundSegment); + } + public void addTextSegment (StringBuilder text) { if (text.length() > 0) { _segs.add(new StringSegment(text.toString())); @@ -212,11 +228,15 @@ public class Mustache case '#': requireNoNewlines(tag, line); return new Accumulator(_compiler) { - public Template.Segment[] finish () { + @Override public boolean skipNewline () { + // if we just opened this section, we want to skip a newline + return (_segs.size() == 0) || super.skipNewline(); + } + @Override public Template.Segment[] finish () { throw new MustacheException("Section missing close tag " + "[line=" + line + ", name=" + tag1 + "]"); } - protected Accumulator addCloseSectionSegment (String itag, int line) { + @Override protected Accumulator addCloseSectionSegment (String itag, int line) { requireSameName(tag1, itag, line); outer._segs.add(new SectionSegment(itag, super.finish())); return outer; @@ -226,11 +246,15 @@ public class Mustache case '^': requireNoNewlines(tag, line); return new Accumulator(_compiler) { - public Template.Segment[] finish () { + @Override public boolean skipNewline () { + // if we just opened this section, we want to skip a newline + return (_segs.size() == 0) || super.skipNewline(); + } + @Override public Template.Segment[] finish () { throw new MustacheException("Inverted section missing close tag " + "[line=" + line + ", name=" + tag1 + "]"); } - protected Accumulator addCloseSectionSegment (String itag, int line) { + @Override protected Accumulator addCloseSectionSegment (String itag, int line) { requireSameName(tag1, itag, line); outer._segs.add(new InvertedSectionSegment(itag, super.finish())); return outer; diff --git a/src/test/java/com/samskivert/mustache/MustacheTest.java b/src/test/java/com/samskivert/mustache/MustacheTest.java index 0443c28..fff9908 100644 --- a/src/test/java/com/samskivert/mustache/MustacheTest.java +++ b/src/test/java/com/samskivert/mustache/MustacheTest.java @@ -147,6 +147,25 @@ public class MustacheTest }); } + @Test public void testNewlineSkipping () { + String tmpl = "list:\n" + + "{{#items}}\n" + + "{{this}}\n" + + "{{/items}}\n" + + "{{^items}}\n" + + "no items\n" + + "{{/items}}\n" + + "endlist"; + test("list:\n" + + "one\n" + + "two\n" + + "three\n" + + "endlist", tmpl, context("items", Arrays.asList("one", "two", "three"))); + test("list:\n" + + "no items\n" + + "endlist", tmpl, context("items", Collections.emptyList())); + } + protected void test (String expected, String template, Object ctx) { assertEquals(expected, Mustache.compiler().compile(template).execute(ctx));