diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java index fee43a6..21d560d 100644 --- a/src/main/java/com/samskivert/mustache/Mustache.java +++ b/src/main/java/com/samskivert/mustache/Mustache.java @@ -470,7 +470,15 @@ else if (seg instanceof FauxSegment) { return segs; } - static Template.Segment[] indentSegs(Template.Segment[] _segs, String indent) { + /** + * Indents segments by indent. + * @param _segs segments to be cloned if indentation is needed + * @param indent the space to use for indent. + * @param _first whether to append an indent on the first segment + * @param _last whether to append an indent on the last segment last empty newline (no character after \n). + * @return cloned segments if changed + */ + static Template.Segment[] indentSegs(Template.Segment[] _segs, String indent, boolean _first, boolean _last) { // unlike trim this method clones the segments if they have changed // so the return value must be handled // a simple identity check on the return can be used to determine @@ -486,38 +494,104 @@ static Template.Segment[] indentSegs(Template.Segment[] _segs, String indent) { Template.Segment seg = _segs[i]; Template.Segment pseg = (i > 0) ? _segs[i-1] : null; Template.Segment nseg = (i < length - 1) ? _segs[i+1] : null; - /* - * If we are the first segment then we rely on the indentation - * already present before the partial tag. - * [ WS ]{{> partial }} - * - * That is partial tags do not have the trailing blank - * removed. - * - * This avoids needlessley creating StringSegment tags. - */ - boolean first = pseg == null ? false : seg.isStandalone() || pseg.isStandalone(); - boolean last; - if (nseg instanceof BlockSegment){ - BlockSegment bs = (BlockSegment) nseg; - last = bs.isStandaloneStart(); + + Template.Segment copy; + if (seg instanceof BlockSegment) { + BlockSegment bs = (BlockSegment) seg; + boolean first; + boolean last; + if (pseg == null) { + // We are the first segment so we inherit + // outer first + first = _first; + } + else if (bs.isStandaloneStart()) { + first = true; + } + else { + first = false; + } + if (bs.isStandalone()) { + // the closing tag owns the last new line + // in the section so we do not indent + last = false; + } + else if (nseg == null) { + // We are the last segment so we inherit the + // outer last + last = _last; + } + else { + last = true; + } + copy = bs.indent(indent, first, last); + } + else if (seg instanceof StringSegment) { + boolean first; + boolean last; + if (pseg == null) { + first = _first; + } + else if (pseg.isStandalone()) { + first = true; + } + else { + first = false; + } + if (nseg == null) { + last = _last; + } + else if (nseg instanceof BlockSegment) { + BlockSegment bs = (BlockSegment) nseg; + last = ! bs.isStandaloneStart(); + } + else if (nseg.isStandalone()) { + last = false; + } + else { + last = true; + } + copy = seg.indent(indent, first, last); } - else if (nseg == null || nseg.isStandalone()) { - last = false; + else if (seg instanceof IncludedTemplateSegment) { + /* + * If we are standalone then we rely on the indentation + * already present before the partial tag. + * [ WS ]{{> partial }}[\n] + * + * That is partial tags do not have the trailing blank + * removed during the trim process. + * + * This avoids needlessley creating StringSegment tags. + */ + if (seg.isStandalone()) { + boolean last; + if (nseg == null) { + last = _last; + } + else if (nseg.isStandalone()) { + last = false; + } + else { + last = true; + } + // Again first = false here because we + //already have the indentation set on a previous segment + copy = seg.indent(indent, false, last); + } + else { + copy = seg; + } } else { - last = true; + copy = seg.indent(indent, _first, _last); } - /* - * Recursively indent - */ - Template.Segment copy = seg.indent(indent, first, last); if (copy != seg) { changed = true; } copySegs[i] = copy; } - if (changed) { + if (changed) { return copySegs; } return _segs; @@ -1162,10 +1236,12 @@ protected SectionSegment(SectionSegment original, Template.Segment[] segs) { } } @Override protected SectionSegment indent (String indent, boolean first, boolean last) { - if (indent.equals("")) { - return this; - } - Template.Segment[] segs = indentSegs(_segs, indent); + // If the end tag is standalone we do NOT add the indent on the end + // (e.g. the newline following the end tag). + // This is because the block always owns the newlines within the block but + // it only owns the last newline following the end tag if and only if + // the closing tag is standalone. + Template.Segment[] segs = indentSegs(_segs, indent, first, last); if (segs == _segs) { return this; } @@ -1222,10 +1298,7 @@ protected InvertedSegment (InvertedSegment original, Template.Segment[] segs) { } @Override protected InvertedSegment indent (String indent, boolean first, boolean last) { - if (indent.equals("")) { - return this; - } - Template.Segment[] segs = indentSegs(_segs, indent); + Template.Segment[] segs = indentSegs(_segs, indent, first, last); if (segs == _segs) { return this; } diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java index 7e74f46..fa04fbb 100644 --- a/src/main/java/com/samskivert/mustache/Template.java +++ b/src/main/java/com/samskivert/mustache/Template.java @@ -176,7 +176,7 @@ protected Template indent(String indent) { if (indent.equals("")) { return this; } - Segment[] copySegs = Mustache.indentSegs(_segs, indent); + Segment[] copySegs = Mustache.indentSegs(_segs, indent, false,false); if (copySegs == _segs) { return this; } diff --git a/src/test/java/com/samskivert/mustache/specs/SpecTest.java b/src/test/java/com/samskivert/mustache/specs/SpecTest.java index 4723c39..2f807c5 100644 --- a/src/test/java/com/samskivert/mustache/specs/SpecTest.java +++ b/src/test/java/com/samskivert/mustache/specs/SpecTest.java @@ -31,7 +31,7 @@ public SpecTest (Spec spec, String name) { public void test () throws Exception { //System.out.println("Testing: " + name); SpecAwareTemplateLoader loader = new SpecAwareTemplateLoader(spec); - Mustache.Compiler compiler = Mustache.compiler().defaultValue("").withLoader(loader); + Mustache.Compiler compiler = Mustache.compiler().emptyStringIsFalse(true).defaultValue("").withLoader(loader); String tmpl = spec.getTemplate(); String desc = String.format("Template: '''%s'''\nData: '%s'\n", uncrlf(tmpl), uncrlf(spec.getData().toString())); diff --git a/src/test/resources/custom/specs/partials.yml b/src/test/resources/custom/specs/partials.yml index 29494a1..94d9a91 100644 --- a/src/test/resources/custom/specs/partials.yml +++ b/src/test/resources/custom/specs/partials.yml @@ -24,8 +24,8 @@ tests: nest: "2\n{{{content}}}\n2\n" expected: "|\n 1\n 2\n <\n->\n 2\n 1\n|\n" - - name: Standalone Indentation - desc: Each line of the partial should be indented before rendering. + - name: Partial Section Indentation End Content + desc: Closing end sections that have content on same line should be indented data: { content: "<\n->" } template: | \ @@ -34,12 +34,83 @@ tests: partials: partial: | | - {{{content}}} + {{#content}} + {{{.}}} + {{/content}}- | expected: | \ | < -> + - | / + + - name: Partial Section Indentation Inside Start Content + desc: Content that is not white space on same line as section start tag inside should be indented + data: { content: "<\n->" } + template: | + \ + {{>partial}} + / + partials: + partial: | + | + {{#content}}- + {{{.}}} + {{/content}} + | + expected: | + \ + | + - + < + -> + | + / + + - name: Partial Section Indentation Start Content + desc: Content that is not white space on same line as section start tags should be indented + data: { content: "<\n->" } + template: | + \ + {{>partial}} + / + partials: + partial: | + | + -{{#content}} + {{{.}}} + {{/content}} + | + expected: | + \ + | + - + < + -> + | + / + + - name: Partial Indentation With Empty Sections + desc: Empty standlone sections should not have indentation before or after + data: { content: "" } + template: | + \ + {{>partial}} + / + partials: + partial: | + | + {{#content}} + {{{.}}} + {{/content}} + | + expected: | + \ + | + | + / + +# The extra newline on the end is required \ No newline at end of file