diff --git a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java index 278dddc1f..e070a695f 100644 --- a/src/main/java/com/hubspot/jinjava/tree/TreeParser.java +++ b/src/main/java/com/hubspot/jinjava/tree/TreeParser.java @@ -60,7 +60,12 @@ public Node buildTree() { Node node = nextNode(); if (node != null) { - parent.getChildren().add(node); + if (node instanceof TextNode && getLastSibling() instanceof TextNode) { + // merge adjacent text nodes so whitespace control properly applies + getLastSibling().getMaster().mergeImageAndContent(node.getMaster()); + } else { + parent.getChildren().add(node); + } } } diff --git a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java index e75e67a18..53d838568 100644 --- a/src/main/java/com/hubspot/jinjava/tree/parse/Token.java +++ b/src/main/java/com/hubspot/jinjava/tree/parse/Token.java @@ -21,7 +21,7 @@ public abstract class Token implements Serializable { private static final long serialVersionUID = 3359084948763661809L; - protected final String image; + protected String image; // useful for some token type protected String content; @@ -50,6 +50,11 @@ public String getImage() { return image; } + public void mergeImageAndContent(Token otherToken) { + this.image = image + otherToken.image; + this.content = content + otherToken.content; + } + public int getLineNumber() { return lineNumber; } diff --git a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java index e73b92fce..2bf2d025a 100644 --- a/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/TreeParserTest.java @@ -25,6 +25,14 @@ public void itStripsRightWhiteSpace() throws Exception { assertThat(interpreter.render(tree)).isEqualTo(".1\n.2\n.3\n"); } + @Test + public void itStripsRightWhiteSpaceWithComment() throws Exception { + String expression = + "{% for foo in [1,2,3] -%} \n {#- comment -#} \n {#- comment -#} .{{ foo }}\n{% endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".1\n.2\n.3\n"); + } + @Test public void itStripsLeftWhiteSpace() throws Exception { String expression = "{% for foo in [1,2,3] %}\n{{ foo }}. \n {%- endfor %}"; @@ -32,6 +40,14 @@ public void itStripsLeftWhiteSpace() throws Exception { assertThat(interpreter.render(tree)).isEqualTo("\n1.\n2.\n3."); } + @Test + public void itStripsLeftWhiteSpaceWithComment() throws Exception { + String expression = + "{% for foo in [1,2,3] %}\n{{ foo }}. \n {#- comment -#} {%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo("\n1.\n2.\n3."); + } + @Test public void itStripsLeftAndRightWhiteSpace() throws Exception { String expression = "{% for foo in [1,2,3] -%} \n .{{ foo }}. \n {%- endfor %}"; @@ -39,6 +55,14 @@ public void itStripsLeftAndRightWhiteSpace() throws Exception { assertThat(interpreter.render(tree)).isEqualTo(".1..2..3."); } + @Test + public void itStripsLeftAndRightWhiteSpaceWithComment() throws Exception { + String expression = + "{% for foo in [1,2,3] -%} \n {#- comment -#} \n {#- comment -#} .{{ foo }}. \n {#- comment -#} \n {#- comment -#} {%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".1..2..3."); + } + @Test public void itPreservesInnerWhiteSpace() throws Exception { String expression = @@ -47,6 +71,14 @@ public void itPreservesInnerWhiteSpace() throws Exception { assertThat(interpreter.render(tree)).isEqualTo("L\n1\nRL\n2\nRL\n3\nR"); } + @Test + public void itPreservesInnerWhiteSpaceWithComment() throws Exception { + String expression = + "{% for foo in [1,2,3] -%}\n {#- comment -#} \n {#- comment -#}L{% if true %}\n{{ foo }}\n{% endif %}R\n {#- comment -#} \n {#- comment -#}{%- endfor %}"; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo("L\n1\nRL\n2\nRL\n3\nR"); + } + @Test public void itStripsLeftWhiteSpaceBeforeTag() throws Exception { String expression = ".\n {%- for foo in [1,2,3] %} {{ foo }} {% endfor %} \n."; @@ -54,6 +86,14 @@ public void itStripsLeftWhiteSpaceBeforeTag() throws Exception { assertThat(interpreter.render(tree)).isEqualTo(". 1 2 3 \n."); } + @Test + public void itStripsLeftWhiteSpaceBeforeTagWithComment() throws Exception { + String expression = + ".\n {#- comment -#} \n {#- comment -#} {%- for foo in [1,2,3] %} {{ foo }} {% endfor %} \n."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(". 1 2 3 \n."); + } + @Test public void itStripsRightWhiteSpaceAfterTag() throws Exception { String expression = ".\n {% for foo in [1,2,3] %} {{ foo }} {% endfor -%} \n."; @@ -61,6 +101,14 @@ public void itStripsRightWhiteSpaceAfterTag() throws Exception { assertThat(interpreter.render(tree)).isEqualTo(".\n 1 2 3 ."); } + @Test + public void itStripsRightWhiteSpaceAfterTagWithComment() throws Exception { + String expression = + ".\n {% for foo in [1,2,3] %} {{ foo }} {% endfor -%} \n {#- comment -#} \n {#- comment -#}."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".\n 1 2 3 ."); + } + @Test public void itStripsAllOuterWhiteSpace() throws Exception { String expression = ".\n {%- for foo in [1,2,3] -%} {{ foo }} {%- endfor -%} \n."; @@ -68,6 +116,14 @@ public void itStripsAllOuterWhiteSpace() throws Exception { assertThat(interpreter.render(tree)).isEqualTo(".123."); } + @Test + public void itStripsAllOuterWhiteSpaceWithComment() throws Exception { + String expression = + ".\n {#- comment -#} \n {#- comment -#} {%- for foo in [1,2,3] -%} {{ foo }} {%- endfor -%} \n {#- comment -#} \n {#- comment -#}."; + final Node tree = new TreeParser(interpreter, expression).buildTree(); + assertThat(interpreter.render(tree)).isEqualTo(".123."); + } + @Test public void trimAndLstripBlocks() { interpreter =