Skip to content

Commit

Permalink
Merge pull request #154 from jstachio/section_indent_fix
Browse files Browse the repository at this point in the history
Fix indentation with section segments
  • Loading branch information
samskivert authored Nov 28, 2023
2 parents 57f182c + 388406e commit 09f7121
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 37 deletions.
137 changes: 105 additions & 32 deletions src/main/java/com/samskivert/mustache/Mustache.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/samskivert/mustache/Template.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/samskivert/mustache/specs/SpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
77 changes: 74 additions & 3 deletions src/test/resources/custom/specs/partials.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
\
Expand All @@ -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

0 comments on commit 09f7121

Please sign in to comment.