diff options
-rw-r--r-- | CHANGES.rst | 2 | ||||
-rw-r--r-- | docs/templates.rst | 9 | ||||
-rw-r--r-- | src/jinja2/lexer.py | 12 | ||||
-rw-r--r-- | tests/test_lexnparse.py | 118 |
4 files changed, 136 insertions, 5 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index d42f213c..298cf0ab 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,7 +13,7 @@ Unreleased :class:`~loaders.PackageLoader`. :issue:`1168` - Fix a bug that caused imported macros to not have access to the current template's globals. :issue:`688` - +- Add ability to ignore ``trim_blocks`` using ``+%}``. :issue:`1036` Version 2.11.2 -------------- diff --git a/docs/templates.rst b/docs/templates.rst index a346ef23..3101d0ed 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -230,6 +230,15 @@ plus sign (``+``) at the start of a block:: {%+ if something %}yay{% endif %} </div> +Similarly, you can manually disable the ``trim_blocks`` behavior by +putting a plus sign (``+``) at the end of a block:: + + <div> + {% if something +%} + yay + {% endif %} + </div> + You can also strip whitespace in templates by hand. If you add a minus sign (``-``) to the start or end of a block (e.g. a :ref:`for-loop` tag), a comment, or a variable expression, the whitespaces before or after diff --git a/src/jinja2/lexer.py b/src/jinja2/lexer.py index 0a992073..082a051d 100644 --- a/src/jinja2/lexer.py +++ b/src/jinja2/lexer.py @@ -509,8 +509,8 @@ class Lexer: TOKEN_COMMENT_BEGIN: [ ( c( - fr"(.*?)((?:\-{comment_end_re}\s*" - fr"|{comment_end_re}){block_suffix_re})" + fr"(.*?)((?:\+{comment_end_re}|\-{comment_end_re}\s*" + fr"|{comment_end_re}{block_suffix_re}))" ), (TOKEN_COMMENT, TOKEN_COMMENT_END), "#pop", @@ -520,7 +520,10 @@ class Lexer: # blocks TOKEN_BLOCK_BEGIN: [ ( - c(fr"(?:\-{block_end_re}\s*|{block_end_re}){block_suffix_re}"), + c( + fr"(?:\+{block_end_re}|\-{block_end_re}\s*" + fr"|{block_end_re}{block_suffix_re})" + ), TOKEN_BLOCK_END, "#pop", ), @@ -540,7 +543,8 @@ class Lexer: ( c( fr"(.*?)((?:{block_start_re}(\-|\+|))\s*endraw\s*" - fr"(?:\-{block_end_re}\s*|{block_end_re}{block_suffix_re}))" + fr"(?:\+{block_end_re}|\-{block_end_re}\s*" + fr"|{block_end_re}{block_suffix_re}))" ), OptionalLStrip(TOKEN_DATA, TOKEN_RAW_END), "#pop", diff --git a/tests/test_lexnparse.py b/tests/test_lexnparse.py index c0257cf1..96e134d1 100644 --- a/tests/test_lexnparse.py +++ b/tests/test_lexnparse.py @@ -903,3 +903,121 @@ ${item} ## the rest of the stuff <!--- endfor -->""" ) assert tmpl.render(seq=range(5)) == "01234" + + +class TestTrimBlocks: + def test_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string(" {% if True %}\n {% endif %}") + assert tmpl.render() == " " + + def test_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == " \n " + + def test_no_trim_outer(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string("{% if True %}X{% endif +%}\nmore things") + assert tmpl.render() == "X\nmore things" + + def test_lstrip_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == "\n" + + def test_trim_blocks_false_with_no_trim(self, env): + # Test that + is a NOP (but does not cause an error) if trim_blocks=False + env = Environment(trim_blocks=False, lstrip_blocks=False) + tmpl = env.from_string(" {% if True %}\n {% endif %}") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == " \n " + + tmpl = env.from_string(" {# comment #}\n ") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {# comment +#}\n ") + assert tmpl.render() == " \n " + + tmpl = env.from_string(" {% raw %}{% endraw %}\n ") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {% raw %}{% endraw +%}\n ") + assert tmpl.render() == " \n " + + def test_trim_nested(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {% if True %}\na {% if True %}\nb {% endif %}\nc {% endif %}" + ) + assert tmpl.render() == "a b c " + + def test_no_trim_nested(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {% if True +%}\na {% if True +%}\nb {% endif +%}\nc {% endif %}" + ) + assert tmpl.render() == "\na \nb \nc " + + def test_comment_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(""" {# comment #}\n\n """) + assert tmpl.render() == "\n " + + def test_comment_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(""" {# comment +#}\n\n """) + assert tmpl.render() == "\n\n " + + def test_multiple_comment_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {# comment #}\n\n{# comment2 #}\n \n{# comment3 #}\n\n " + ) + assert tmpl.render() == "\n \n\n " + + def test_multiple_comment_no_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {# comment +#}\n\n{# comment2 +#}\n \n{# comment3 +#}\n\n " + ) + assert tmpl.render() == "\n\n\n \n\n\n " + + def test_raw_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw %}\n\n{{ y }}") + assert tmpl.render(x=1, y=2) == "1\n\n\n2" + + def test_raw_no_trim_lstrip(self, env): + env = Environment(trim_blocks=False, lstrip_blocks=True) + tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw +%}\n\n{{ y }}") + assert tmpl.render(x=1, y=2) == "1\n\n\n\n2" + + # raw blocks do not process inner text, so start tag cannot ignore trim + with pytest.raises(TemplateSyntaxError): + tmpl = env.from_string("{{x}}{% raw +%}\n\n {% endraw +%}\n\n{{ y }}") + + def test_no_trim_angle_bracket(self, env): + env = Environment( + "<%", "%>", "${", "}", "<%#", "%>", lstrip_blocks=True, trim_blocks=True, + ) + tmpl = env.from_string(" <% if True +%>\n\n <% endif %>") + assert tmpl.render() == "\n\n" + + tmpl = env.from_string(" <%# comment +%>\n\n ") + assert tmpl.render() == "\n\n " + + def test_no_trim_php_syntax(self, env): + env = Environment( + "<?", + "?>", + "<?=", + "?>", + "<!--", + "-->", + lstrip_blocks=False, + trim_blocks=True, + ) + tmpl = env.from_string(" <? if True +?>\n\n <? endif ?>") + assert tmpl.render() == " \n\n " + tmpl = env.from_string(" <!-- comment +-->\n\n ") + assert tmpl.render() == " \n\n " |