added support for a new whitespace trimming option

This commit is contained in:
Fabien Potencier
2019-04-03 10:10:47 +02:00
parent 7e3ec0fbee
commit 96eab472d6
9 changed files with 215 additions and 37 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
* 1.39.0 (2019-XX-XX)
* n/a
* added support for a new whitespace trimming option ({%~ ~%}, {{~ ~}}, {#~ ~#})
* 1.38.4 (2019-03-23)
+39 -22
View File
@@ -844,25 +844,28 @@ Whitespace Control
.. versionadded:: 1.1
Tag level whitespace control was added in Twig 1.1.
The first newline after a template tag is removed automatically (like in PHP.)
.. versionadded:: 1.39
Tag level Line whitespace control was added in Twig 1.39.
The first newline after a template tag is removed automatically (like in PHP).
Whitespace is not further modified by the template engine, so each whitespace
(spaces, tabs, newlines etc.) is returned unchanged.
Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
You can also control whitespace on a per tag level. By using the whitespace
control modifiers on your tags, you can trim leading and or trailing whitespace.
.. code-block:: jinja
Twig supports two modifiers:
{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}
* *Whitespace trimming* via the ``-`` modifier: Removes all whitespace
(including newlines);
{# output will be <div><strong>foo bar</strong></div> #}
* *Line whitespace trimming* via the ``~`` modifier: Removes all whitespace
(excluding newlines). Using this modifier on the right disables the default
removal of the first newline inherited from PHP.
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 whitespace:
The modifiers can be used on either side of the tags like in ``{%-`` or ``-%}``
and they consume all whitespace for that side of the tag. It is possible to use
the modifiers on one side of a tag or on both sides:
.. code-block:: jinja
@@ -871,21 +874,35 @@ leading and or trailing whitespace:
{%- if true -%}
{{- value -}}
{%- endif -%}
{# output 'no spaces' #}
The above sample shows the default whitespace control modifier, and how you can
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' %}
<li> {{- value }} </li>
<li>
{{ value }} </li>
{# outputs '<li>\n no spaces </li>' #}
<li>
{{- value }} </li>
{# outputs '<li>no spaces </li>' #}
<li>
{{~ value }} </li>
{# outputs '<li>\nno spaces </li>' #}
.. tip::
In addition to the whitespace modifiers, Twig also has a ``spaceless`` filter
that removes whitespace **between HTML tags**:
.. code-block:: jinja
{% filter spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endfilter %}
{# output will be <div><strong>foo bar</strong></div> #}
Extensions
----------
+40 -8
View File
@@ -62,6 +62,8 @@ class Lexer implements \Twig_LexerInterface
'tag_block' => ['{%', '%}'],
'tag_variable' => ['{{', '}}'],
'whitespace_trim' => '-',
'whitespace_line_trim' => '~',
'whitespace_line_chars' => ' \t\0\x0B',
'interpolation' => ['#{', '}'],
], $options);
@@ -72,6 +74,8 @@ class Lexer implements \Twig_LexerInterface
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1]).'\s*'. // -}}\s*
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_variable'][1]).'['.$this->options['whitespace_line_chars'].']*'. // ~}}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_variable'][1]). // }}
')
}Ax',
@@ -82,22 +86,27 @@ class Lexer implements \Twig_LexerInterface
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1]).'\s*\n?'. // -%}\s*\n?
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1]).'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1]).'\n?'. // %}\n?
')
}Ax',
// {% endverbatim %}
'lex_raw_data' => '{
('.
preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim']). // {%-
'lex_raw_data' => '{'.
preg_quote($this->options['tag_block'][0]). // {%
'('.
$this->options['whitespace_trim']. // -
'|'.
preg_quote($this->options['tag_block'][0]). // {%
')\s*'.
$this->options['whitespace_line_trim']. // ~
')?\s*'.
'(?:end%s)'. // endraw or endverbatim
'\s*'.
'(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1]).'\s*'. // -%}
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1]).'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1]). // %}
')
}sx',
@@ -109,6 +118,8 @@ class Lexer implements \Twig_LexerInterface
(?:'.
preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n?
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_comment'][1], '#').'\n?'. // #}\n?
')
}sx',
@@ -121,6 +132,8 @@ class Lexer implements \Twig_LexerInterface
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1]).'\s*'. // -%}\s*
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1]).'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1]). // %}
')
}Asx',
@@ -137,6 +150,8 @@ class Lexer implements \Twig_LexerInterface
preg_quote($this->options['tag_comment'][0], '#'). // {#
')('.
preg_quote($this->options['whitespace_trim']). // -
'|'.
preg_quote($this->options['whitespace_line_trim']). // ~
')?
}sx',
'interpolation_start' => '{'.preg_quote($this->options['interpolation'][0]).'\s*}A',
@@ -240,8 +255,17 @@ class Lexer implements \Twig_LexerInterface
// push the template text first
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
// trim?
if (isset($this->positions[2][$this->position][0])) {
$text = rtrim($text);
if ($this->options['whitespace_trim'] === $this->positions[2][$this->position][0]) {
// whitespace_trim detected ({%-, {{- or {#-)
$text = rtrim($text);
} else {
// whitespace_line_trim detected ({%~, {{~ or {#~)
// don't trim \r and \n
$text = rtrim($text, " \t\0\x0B");
}
}
$this->pushToken(Token::TEXT_TYPE, $text);
$this->moveCursor($textContent.$position[0]);
@@ -378,8 +402,16 @@ class Lexer implements \Twig_LexerInterface
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
$this->moveCursor($text.$match[0][0]);
if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
$text = rtrim($text);
// trim?
if (isset($match[1][0])) {
if ($this->options['whitespace_trim'] === $match[1][0]) {
// whitespace_trim detected ({%-, {{- or {#-)
$text = rtrim($text);
} else {
// whitespace_line_trim detected ({%~, {{~ or {#~)
// don't trim \r and \n
$text = rtrim($text, " \t\0\x0B");
}
}
$this->pushToken(Token::TEXT_TYPE, $text);
@@ -1,9 +1,6 @@
--TEST--
Whitespace trimming on tags.
--TEMPLATE--
{{ 5 * '{#-'|length }}
{{ '{{-'|length * 5 + '{%-'|length }}
Trim on control tag:
{% for i in range(1, 9) -%}
{{ i }}
@@ -53,9 +50,6 @@ end
--DATA--
return ['leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both']
--EXPECT--
15
18
Trim on control tag:
123456789
@@ -0,0 +1,10 @@
--TEST--
Whitespace trimming as strings.
--TEMPLATE--
{{ 5 * '{#-'|length }}
{{ '{{-'|length * 5 + '{%-'|length }}
--DATA--
return []
--EXPECT--
15
18
@@ -0,0 +1,32 @@
--TEST--
Whitespace trimming on tags (left side).
--TEMPLATE--
**{% if true %}
foo
{%- endif %}**
**
{{- 'foo' }}**
**
{#- comment #}**
**{% verbatim %}
foo
{%- endverbatim %}**
--DATA--
return []
--EXPECT--
**foo**
**foo**
****
**
foo**
@@ -0,0 +1,33 @@
--TEST--
Line whitespace trimming on tags (left side).
--TEMPLATE--
**{% if true %}
foo
{%~ endif %}**
**
{{~ 'foo' }}**
**
{#~ comment #}**
**{% verbatim %}
foo
{%~ endverbatim %}**
--DATA--
return []
--EXPECT--
**foo
**
**
foo**
**
**
**
foo
**
@@ -0,0 +1,32 @@
--TEST--
Line whitespace trimming on tags (right side).
--TEMPLATE--
**{% if true ~%}
foo{% endif %}**
**{{ 'foo' ~}}
foo
**
**{# comment ~#}
foo
**
**{% verbatim ~%}
foo{% endverbatim %}**
--DATA--
return []
--EXPECT--
**
foo**
**foo
foo
**
**
foo
**
**
foo**
@@ -0,0 +1,28 @@
--TEST--
Whitespace trimming on tags (right side).
--TEMPLATE--
**{% if true -%}
foo{% endif %}**
**{{ 'foo' -}}
**
**{# comment -#}
**
**{% verbatim -%}
foo{% endverbatim %}**
--DATA--
return []
--EXPECT--
**foo**
**foo**
****
**foo**