From 22cf53fdfa3dc34e7e612f8eebba77a16ef91ace Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 22 Mar 2011 22:35:54 -0400 Subject: [PATCH 01/14] Implementing {%- which trims non-newline whitespace preceding it. Refs --- lib/Twig/Lexer.php | 19 ++++++++++++++++--- test/Twig/Tests/Fixtures/tags/trim_block.test | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test/Twig/Tests/Fixtures/tags/trim_block.test diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 93647f69c..6d66e87fe 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -47,6 +47,7 @@ class Twig_Lexer implements Twig_LexerInterface $this->options = array_merge(array( 'tag_comment' => array('{#', '#}'), 'tag_block' => array('{%', '%}'), + 'tag_trim_block' => array('{%-', '-%}'), 'tag_variable' => array('{{', '}}'), ), $options); } @@ -109,6 +110,7 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexData() { + $trimBlock = false; $pos = $this->end; if (false !== ($tmpPos = strpos($this->code, $this->options['tag_comment'][0], $this->cursor)) && $tmpPos < $pos) { $pos = $tmpPos; @@ -118,6 +120,11 @@ class Twig_Lexer implements Twig_LexerInterface $pos = $tmpPos; $token = $this->options['tag_variable'][0]; } + if (false !== ($tmpPos = strpos($this->code, $this->options['tag_trim_block'][0], $this->cursor)) && $tmpPos < $pos) { + $pos = $tmpPos; + $token = $this->options['tag_trim_block'][0]; + $trimBlock = true; + } if (false !== ($tmpPos = strpos($this->code, $this->options['tag_block'][0], $this->cursor)) && $tmpPos < $pos) { $pos = $tmpPos; $token = $this->options['tag_block'][0]; @@ -131,9 +138,12 @@ class Twig_Lexer implements Twig_LexerInterface } // push the template text first - $text = substr($this->code, $this->cursor, $pos - $this->cursor); + $text = $textContent = substr($this->code, $this->cursor, $pos - $this->cursor); + if (true === $trimBlock) { + $text = rtrim($text, " \t"); + } $this->pushToken(Twig_Token::TEXT_TYPE, $text); - $this->moveCursor($text.$token); + $this->moveCursor($textContent . $token); switch ($token) { case $this->options['tag_comment'][0]: @@ -152,6 +162,7 @@ class Twig_Lexer implements Twig_LexerInterface break; case $this->options['tag_block'][0]: + case $this->options['tag_trim_block'][0]: // raw data? if (preg_match('/\s*raw\s*'.preg_quote($this->options['tag_block'][1], '/').'(.*?)'.preg_quote($this->options['tag_block'][0], '/').'\s*endraw\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::TEXT_TYPE, $match[1]); @@ -172,7 +183,9 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexBlock() { - if (empty($this->brackets) && preg_match('/\s*'.preg_quote($this->options['tag_block'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { + $endTag = preg_quote($this->options['tag_block'][1], '/'); + if (empty($this->brackets) && preg_match('/\s*'. $endTag.'/A', $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test new file mode 100644 index 000000000..de26a2cd0 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -0,0 +1,14 @@ +--TEST-- +Whitespace trimming on tags. +--TEMPLATE-- + +--DATA-- +return array('string' => 'a value') +--EXPECT-- + From a615a3cc88e5b6ae3bec29b8dfb781f3a3d36794 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 22 Mar 2011 22:59:56 -0400 Subject: [PATCH 02/14] Adding a test that uses tabs. Added a test for trailing tabs and -%} Refs #284 --- lib/Twig/Lexer.php | 4 +++- test/Twig/Tests/Fixtures/tags/trim_block.test | 12 +++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 6d66e87fe..ef8b33dc7 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -183,8 +183,10 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexBlock() { + $trimTag = preg_quote($this->options['tag_trim_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*'. $endTag.'/A', $this->code, $match, null, $this->cursor)) { + + if (empty($this->brackets) && preg_match('/\s*'. $trimTag . '\h*|\s*' .$endTag.'/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test index de26a2cd0..341a28179 100644 --- a/test/Twig/Tests/Fixtures/tags/trim_block.test +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -6,9 +6,19 @@ Whitespace trimming on tags.
  • {{ string }}
  • {%- endif %} + + --DATA-- -return array('string' => 'a value') +return array('string' => 'a value', 'trailing' => 'trailing tabs') --EXPECT-- + + \ No newline at end of file From 36516f67f2774dc4c72c4584f881b38a2ce9424e Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 22 Mar 2011 23:08:51 -0400 Subject: [PATCH 03/14] Adding a test case for mixing {%- and %} tags together. --- test/Twig/Tests/Fixtures/tags/trim_block.test | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test index 341a28179..ccd532982 100644 --- a/test/Twig/Tests/Fixtures/tags/trim_block.test +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -12,8 +12,14 @@ Whitespace trimming on tags.
  • {{ trailing }}
  • {%- endif -%} + +
      + {%- if mixed %} +
    • {{ mixed }}
    • + {% endif -%} +
    --DATA-- -return array('string' => 'a value', 'trailing' => 'trailing tabs') +return array('string' => 'a value', 'trailing' => 'trailing tabs', 'mixed' => 'mixed tags') --EXPECT--
    • a value
    • @@ -21,4 +27,8 @@ return array('string' => 'a value', 'trailing' => 'trailing tabs')
      • trailing tabs
      • -
      \ No newline at end of file +
    + +
      +
    • mixed tags
    • +
    From bc4d8b32cd226d925d7340b87246919dcabd5b61 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 30 Mar 2011 07:32:06 -0400 Subject: [PATCH 04/14] Fixing use of non-existent method name. --- test/Twig/Tests/integrationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Twig/Tests/integrationTest.php b/test/Twig/Tests/integrationTest.php index 63d49eace..2463bf5ad 100644 --- a/test/Twig/Tests/integrationTest.php +++ b/test/Twig/Tests/integrationTest.php @@ -53,7 +53,7 @@ class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase try { $template = $twig->loadTemplate('index.twig'); } catch (Twig_Error_Syntax $e) { - $e->setFilename($file); + $e->setTemplateFile($file); throw $e; } catch (Exception $e) { From d9749fcb31d4cf5de3d88c58b20664e8c3f2c13f Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 30 Mar 2011 07:33:04 -0400 Subject: [PATCH 05/14] Adding whitespace trimming support to other tag types (comments, variables) Refactoring how whitespace trimming is done, and moving it into a separate option that is combined with other tag types. Adding more tests. Refs #284 --- lib/Twig/Lexer.php | 61 +++++++++++-------- test/Twig/Tests/Fixtures/tags/trim_block.test | 27 ++++++-- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index ef8b33dc7..2fe81e587 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -47,8 +47,8 @@ class Twig_Lexer implements Twig_LexerInterface $this->options = array_merge(array( 'tag_comment' => array('{#', '#}'), 'tag_block' => array('{%', '%}'), - 'tag_trim_block' => array('{%-', '-%}'), 'tag_variable' => array('{{', '}}'), + 'whitespace_trim' => '-' ), $options); } @@ -110,24 +110,21 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexData() { - $trimBlock = false; $pos = $this->end; - if (false !== ($tmpPos = strpos($this->code, $this->options['tag_comment'][0], $this->cursor)) && $tmpPos < $pos) { - $pos = $tmpPos; - $token = $this->options['tag_comment'][0]; - } - if (false !== ($tmpPos = strpos($this->code, $this->options['tag_variable'][0], $this->cursor)) && $tmpPos < $pos) { - $pos = $tmpPos; - $token = $this->options['tag_variable'][0]; - } - if (false !== ($tmpPos = strpos($this->code, $this->options['tag_trim_block'][0], $this->cursor)) && $tmpPos < $pos) { - $pos = $tmpPos; - $token = $this->options['tag_trim_block'][0]; - $trimBlock = true; - } - if (false !== ($tmpPos = strpos($this->code, $this->options['tag_block'][0], $this->cursor)) && $tmpPos < $pos) { - $pos = $tmpPos; - $token = $this->options['tag_block'][0]; + $append = ''; + + // Find the first token after the cursor. + foreach (array('tag_comment', 'tag_variable', 'tag_block') as $type) { + $tmpPos = strpos($this->code, $this->options[$type][0], $this->cursor); + if (false !== $tmpPos && $tmpPos < $pos) { + $trimBlock = false; + $pos = $tmpPos; + $token = $this->options[$type][0]; + if (strpos($this->code, $this->options['whitespace_trim'], $pos) === ($pos + strlen($token))) { + $trimBlock = true; + $append = $this->options['whitespace_trim']; + } + } } // if no matches are left we return the rest of the template as simple text token @@ -136,22 +133,31 @@ class Twig_Lexer implements Twig_LexerInterface $this->cursor = $this->end; return; } - + // push the template text first $text = $textContent = substr($this->code, $this->cursor, $pos - $this->cursor); if (true === $trimBlock) { $text = rtrim($text, " \t"); } $this->pushToken(Twig_Token::TEXT_TYPE, $text); - $this->moveCursor($textContent . $token); + $this->moveCursor($textContent . $token . $append); switch ($token) { case $this->options['tag_comment'][0]: - if (false === $pos = strpos($this->code, $this->options['tag_comment'][1], $this->cursor)) { + $endPos = strpos($this->code, $this->options['tag_comment'][1], $this->cursor); + if (false === $endPos) { throw new Twig_Error_Syntax('unclosed comment', $this->lineno, $this->filename); } + $trimLen = strlen($this->options['whitespace_trim']); + if (strpos($this->code, $this->options['whitespace_trim'], $endPos - $trimLen) === $endPos - $trimLen) { + $endTag = preg_quote($this->options['tag_comment'][1], '/'); + preg_match('/' . $endTag . '(\h*)/', $this->code, $match, null, $this->cursor); + if (isset($match[1])) { + $endPos += strlen($match[1]); + } + } - $this->moveCursor(substr($this->code, $this->cursor, $pos - $this->cursor) . $this->options['tag_comment'][1]); + $this->moveCursor(substr($this->code, $this->cursor, $endPos - $this->cursor) . $this->options['tag_comment'][1]); // mimics the behavior of PHP by removing the newline that follows instructions if present if ("\n" === substr($this->code, $this->cursor, 1)) { @@ -162,7 +168,6 @@ class Twig_Lexer implements Twig_LexerInterface break; case $this->options['tag_block'][0]: - case $this->options['tag_trim_block'][0]: // raw data? if (preg_match('/\s*raw\s*'.preg_quote($this->options['tag_block'][1], '/').'(.*?)'.preg_quote($this->options['tag_block'][0], '/').'\s*endraw\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::TEXT_TYPE, $match[1]); @@ -183,11 +188,10 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexBlock() { - $trimTag = preg_quote($this->options['tag_trim_block'][1], '/'); + $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - - if (empty($this->brackets) && preg_match('/\s*'. $trimTag . '\h*|\s*' .$endTag.'/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*'. $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; @@ -205,7 +209,10 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexVar() { - if (empty($this->brackets) && preg_match('/\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { + $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_variable'][1], '/'); + $endTag = preg_quote($this->options['tag_variable'][1], '/'); + + if (empty($this->brackets) && preg_match('/\s*' . $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::VAR_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test index ccd532982..54cb72773 100644 --- a/test/Twig/Tests/Fixtures/tags/trim_block.test +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -1,28 +1,38 @@ --TEST-- Whitespace trimming on tags. --TEMPLATE-- + {#- Comments can trim leading space #}
      - {%- if string %} -
    • {{ string }}
    • + {%- if leading %} +
    • {{- leading }}
    • {%- endif %}
    +{# Comments can trim trailing space -#}
      {%- if trailing -%} -
    • {{ trailing }}
    • +
    • {{ trailing -}}
    • {%- endif -%}
      {%- if mixed %} -
    • {{ mixed }}
    • +
    • {{- mixed }}
    • {% endif -%}
    + + {#- Comments can trim both -#} +
      + {%- if both -%} +
    • {{- both -}}
    • + {%- endif -%} +
    +after --DATA-- -return array('string' => 'a value', 'trailing' => 'trailing tabs', 'mixed' => 'mixed tags') +return array('leading' => 'leading space', 'trailing' => 'trailing tabs', 'mixed' => 'mixed tags', 'both' => 'both') --EXPECT--
      -
    • a value
    • +
    • leading space
      @@ -32,3 +42,8 @@ return array('string' => 'a value', 'trailing' => 'trailing tabs', 'mixed' => 'm
      • mixed tags
      + +
        +
      • both
      • +
      +after \ No newline at end of file From 8f158bdda930bf2541c6e746fe82b97481492bc2 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 30 Mar 2011 21:55:15 -0400 Subject: [PATCH 06/14] Adding a bit of documentation for whitespace trimming. --- doc/templates.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/templates.rst b/doc/templates.rst index 5c21473c7..1869f63bf 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -160,6 +160,26 @@ Use the ``spaceless`` tag to remove whitespace between HTML tags: {# output will be
      foo
      #} +In addition to the spaceless tag you can also control whitespace on a per tag +level. By using the whitespace control modifier on your tags you can trim +leading and or trailing horizontal whitespace from any tag type: + +.. code-block:: jinja + + {#- No leading/trailing whitespace -#} + {%- if condition %-} + {{- value -}} + {%- endif -%} + +The above sample shows the default whitespace control modifier, and how you can +use it to remove whitespace around tags. Only horizontal space will be removed +any vertical space (newlines) will need to be removed with the +``spaceless`` tag. + +..versionadded:: 1.1 + + Tag level whitespace control was added in 1.1 + Escaping -------- From 08959ab5daa09d2b5b6475a4e97090be250c30ef Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 31 Mar 2011 21:24:00 -0400 Subject: [PATCH 07/14] Fixing typo. --- doc/templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/templates.rst b/doc/templates.rst index 1869f63bf..da8de8949 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -167,7 +167,7 @@ leading and or trailing horizontal whitespace from any tag type: .. code-block:: jinja {#- No leading/trailing whitespace -#} - {%- if condition %-} + {%- if condition -%} {{- value -}} {%- endif -%} From 86b11197c05f906d081f4db4a303e7ee8ddc1c4f Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 2 Apr 2011 10:05:55 -0400 Subject: [PATCH 08/14] Fixing some whitespace. --- lib/Twig/Lexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 2fe81e587..ba7e4570b 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -191,7 +191,7 @@ class Twig_Lexer implements Twig_LexerInterface $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*'. $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*' . $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; From d0b40aff672c3905455c84a8ce6677c5a0c1e469 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 2 Apr 2011 10:08:51 -0400 Subject: [PATCH 09/14] Applying changes suggested by nikic to simplify how end of comments are processed. --- lib/Twig/Lexer.php | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index ba7e4570b..0664e8d85 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -144,27 +144,15 @@ class Twig_Lexer implements Twig_LexerInterface switch ($token) { case $this->options['tag_comment'][0]: - $endPos = strpos($this->code, $this->options['tag_comment'][1], $this->cursor); - if (false === $endPos) { + $commentEndRegex = '/.*?(?:' . preg_quote($this->options['whitespace_trim'], '/') + . preg_quote($this->options['tag_comment'][1], '/') . '\h*|' + . preg_quote($this->options['tag_comment'][1], '/') . ')\n?/As'; + + if (!preg_match($commentEndRegex, $this->code, $match, null, $this->cursor)) { throw new Twig_Error_Syntax('unclosed comment', $this->lineno, $this->filename); } - $trimLen = strlen($this->options['whitespace_trim']); - if (strpos($this->code, $this->options['whitespace_trim'], $endPos - $trimLen) === $endPos - $trimLen) { - $endTag = preg_quote($this->options['tag_comment'][1], '/'); - preg_match('/' . $endTag . '(\h*)/', $this->code, $match, null, $this->cursor); - if (isset($match[1])) { - $endPos += strlen($match[1]); - } - } - - $this->moveCursor(substr($this->code, $this->cursor, $endPos - $this->cursor) . $this->options['tag_comment'][1]); - - // mimics the behavior of PHP by removing the newline that follows instructions if present - if ("\n" === substr($this->code, $this->cursor, 1)) { - ++$this->cursor; - ++$this->lineno; - } + $this->moveCursor($match[0]); break; case $this->options['tag_block'][0]: From 650d0f2948037c1053e295c2b2af0938bf805213 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 2 Apr 2011 10:11:44 -0400 Subject: [PATCH 10/14] Moving newline trimming into the regexp used to match end of tags. --- lib/Twig/Lexer.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 0664e8d85..8df232451 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -179,16 +179,10 @@ class Twig_Lexer implements Twig_LexerInterface $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*' . $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*(' . $trimTag . '\h*|\s*' . $endTag . ')\n?/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; - - // mimics the behavior of PHP by removing the newline that follows instructions if present - if ("\n" === substr($this->code, $this->cursor, 1)) { - ++$this->cursor; - ++$this->lineno; - } } else { $this->lexExpression(); From 86bc75bfe46c9b476a65f26200284cd4c78c5afe Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 5 Apr 2011 21:06:58 -0400 Subject: [PATCH 11/14] Making a capturing group non capturing, as the group isn't used. --- lib/Twig/Lexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 8df232451..eb7c4c256 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -179,7 +179,7 @@ class Twig_Lexer implements Twig_LexerInterface $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*(' . $trimTag . '\h*|\s*' . $endTag . ')\n?/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*(?:' . $trimTag . '\h*|\s*' . $endTag . ')\n?/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; From ec052b3ae0b7c361facd0a127afda705409b1871 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 5 Apr 2011 21:29:38 -0400 Subject: [PATCH 12/14] Adding additional test suggested by nikic. --- lib/Twig/Lexer.php | 1 + test/Twig/Tests/Fixtures/tags/trim_block.test | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index eb7c4c256..d03d14b49 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -118,6 +118,7 @@ class Twig_Lexer implements Twig_LexerInterface $tmpPos = strpos($this->code, $this->options[$type][0], $this->cursor); if (false !== $tmpPos && $tmpPos < $pos) { $trimBlock = false; + $append = ''; $pos = $tmpPos; $token = $this->options[$type][0]; if (strpos($this->code, $this->options['whitespace_trim'], $pos) === ($pos + strlen($token))) { diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test index 54cb72773..8e3152184 100644 --- a/test/Twig/Tests/Fixtures/tags/trim_block.test +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -28,6 +28,8 @@ Whitespace trimming on tags. {%- endif -%}
    after +{{ 5 * '{#-'|length }} +{{ '{{-'|length * 5 + '{%-'|length }} --DATA-- return array('leading' => 'leading space', 'trailing' => 'trailing tabs', 'mixed' => 'mixed tags', 'both' => 'both') --EXPECT-- @@ -46,4 +48,6 @@ return array('leading' => 'leading space', 'trailing' => 'trailing tabs', 'mixed
    • both
    -after \ No newline at end of file +after +15 +18 From 99e3246c497b05a998c041c6247bacf0a9e72309 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 13 Apr 2011 22:10:57 -0400 Subject: [PATCH 13/14] Making trim tags consume all whitespace, both horizontal and vertical. --- lib/Twig/Lexer.php | 8 +- test/Twig/Tests/Fixtures/tags/trim_block.test | 109 +++++++++++------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index d03d14b49..16dda512c 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -138,7 +138,7 @@ class Twig_Lexer implements Twig_LexerInterface // push the template text first $text = $textContent = substr($this->code, $this->cursor, $pos - $this->cursor); if (true === $trimBlock) { - $text = rtrim($text, " \t"); + $text = rtrim($text); } $this->pushToken(Twig_Token::TEXT_TYPE, $text); $this->moveCursor($textContent . $token . $append); @@ -146,7 +146,7 @@ class Twig_Lexer implements Twig_LexerInterface switch ($token) { case $this->options['tag_comment'][0]: $commentEndRegex = '/.*?(?:' . preg_quote($this->options['whitespace_trim'], '/') - . preg_quote($this->options['tag_comment'][1], '/') . '\h*|' + . preg_quote($this->options['tag_comment'][1], '/') . '\s*|' . preg_quote($this->options['tag_comment'][1], '/') . ')\n?/As'; if (!preg_match($commentEndRegex, $this->code, $match, null, $this->cursor)) { @@ -180,7 +180,7 @@ class Twig_Lexer implements Twig_LexerInterface $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_block'][1], '/'); $endTag = preg_quote($this->options['tag_block'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*(?:' . $trimTag . '\h*|\s*' . $endTag . ')\n?/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*(?:' . $trimTag . '\s*|\s*' . $endTag . ')\n?/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; @@ -195,7 +195,7 @@ class Twig_Lexer implements Twig_LexerInterface $trimTag = preg_quote($this->options['whitespace_trim'] . $this->options['tag_variable'][1], '/'); $endTag = preg_quote($this->options['tag_variable'][1], '/'); - if (empty($this->brackets) && preg_match('/\s*' . $trimTag . '\h*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*' . $trimTag . '\s*|\s*' . $endTag . '/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::VAR_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; diff --git a/test/Twig/Tests/Fixtures/tags/trim_block.test b/test/Twig/Tests/Fixtures/tags/trim_block.test index 8e3152184..1d2273f88 100644 --- a/test/Twig/Tests/Fixtures/tags/trim_block.test +++ b/test/Twig/Tests/Fixtures/tags/trim_block.test @@ -1,53 +1,74 @@ --TEST-- Whitespace trimming on tags. --TEMPLATE-- - {#- Comments can trim leading space #} -
      - {%- if leading %} -
    • {{- leading }}
    • - {%- endif %} -
    - -{# Comments can trim trailing space -#} -
      - {%- if trailing -%} -
    • {{ trailing -}}
    • - {%- endif -%} -
    - -
      - {%- if mixed %} -
    • {{- mixed }}
    • - {% endif -%} -
    - - {#- Comments can trim both -#} -
      - {%- if both -%} -
    • {{- both -}}
    • - {%- endif -%} -
    -after {{ 5 * '{#-'|length }} {{ '{{-'|length * 5 + '{%-'|length }} + +Trim on control tag: +{% for i in range(1, 9) -%} + {{ i }} +{%- endfor %} + + +Trim on output tag: +{% for i in range(1, 9) %} + {{- i -}} +{% endfor %} + + +Trim comments: + +{#- Invisible -#} + +After the comment. + +Trim leading space: +{% if leading %} + + {{- leading }} +{% endif %} + +{%- if leading %} + {{- leading }} + +{%- endif %} + + +Trim trailing space: +{% if trailing -%} + {{ trailing -}} + +{% endif -%} + +Combined: + +{%- if both -%} +
      +
    • {{- both -}}
    • +
    + +{%- endif -%} + +end --DATA-- -return array('leading' => 'leading space', 'trailing' => 'trailing tabs', 'mixed' => 'mixed tags', 'both' => 'both') +return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both') --EXPECT-- -
      -
    • leading space
    • -
    - -
      -
    • trailing tabs
    • -
    - -
      -
    • mixed tags
    • -
    - -
      -
    • both
    • -
    -after 15 18 + +Trim on control tag: +123456789 + +Trim on output tag: +123456789 + +Trim comments:After the comment. + +Trim leading space: +leading space +leading space + +Trim trailing space: +trailing spaceCombined:
      +
    • both
    • +
    end From 485213a22bab3f5f8014181c8174a7bed8554e7b Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 13 Apr 2011 22:15:32 -0400 Subject: [PATCH 14/14] Updating documentation for whitespace trim tags. --- doc/templates.rst | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/templates.rst b/doc/templates.rst index da8de8949..e0b3a2dbb 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -162,19 +162,29 @@ Use the ``spaceless`` tag to remove whitespace between HTML tags: In addition to the spaceless tag you can also control whitespace on a per tag level. By using the whitespace control modifier on your tags you can trim -leading and or trailing horizontal whitespace from any tag type: +leading and or trailing whitespace from any tag type: .. code-block:: jinja + {% set value = 'no spaces' %} {#- No leading/trailing whitespace -#} - {%- if condition -%} + {%- if true -%} {{- value -}} {%- endif -%} + {# output 'spaces' #} + The above sample shows the default whitespace control modifier, and how you can -use it to remove whitespace around tags. Only horizontal space will be removed -any vertical space (newlines) will need to be removed with the -``spaceless`` tag. +use it to remove whitespace around tags. Trimming space will consume all whitespace +for that side of the tag. It is possible to use whitespace trimming on one side +of a tag: + +.. code-block:: jinja + + {% set value = 'no spaces' %} +
  • {{- value }}
  • + + {# outputs '
  • value
  • ' #} ..versionadded:: 1.1