template_parser: Add support for jinja2 whitespace markers.

Fixes #11516.
This commit is contained in:
Rafid Aslam 2019-04-16 17:54:01 +07:00 committed by Tim Abbott
parent d4893dfcbc
commit d983611919
3 changed files with 106 additions and 5 deletions

View File

@ -39,7 +39,9 @@ def pretty_print_html(html, num_spaces=4):
for token in tokens: for token in tokens:
if token.kind in ('html_start', 'handlebars_start', 'handlebars_singleton', if token.kind in ('html_start', 'handlebars_start', 'handlebars_singleton',
'html_singleton', 'django_start') and stack[-1]['tag'] != 'pre': 'html_singleton', 'django_start',
'jinja2_whitespace_stripped_type2_start',
'jinja2_whitespace_stripped_start') and stack[-1]['tag'] != 'pre':
# An HTML start tag should only cause a new indent if we # An HTML start tag should only cause a new indent if we
# are on a new line. # are on a new line.
if (token.tag not in ('extends', 'include', 'else', 'elif') and if (token.tag not in ('extends', 'include', 'else', 'elif') and
@ -50,8 +52,12 @@ def pretty_print_html(html, num_spaces=4):
if is_block: if is_block:
if (((token.kind == 'handlebars_start' and if (((token.kind == 'handlebars_start' and
stack[-1]['token_kind'] == 'handlebars_start') or stack[-1]['token_kind'] == 'handlebars_start') or
(token.kind == 'django_start' and (token.kind in {'django_start',
stack[-1]['token_kind'] == 'django_start')) and 'jinja2_whitespace_stripped_type2_start',
'jinja2_whitespace_stripped_start'} and
stack[-1]['token_kind'] in {'django_start',
'jinja2_whitespace_stripped_type2_start',
'jinja2_whitespace_stripped_start'})) and
not stack[-1]['indenting']): not stack[-1]['indenting']):
info = stack.pop() info = stack.pop()
info['depth'] = info['depth'] + 1 info['depth'] = info['depth'] + 1
@ -94,7 +100,8 @@ def pretty_print_html(html, num_spaces=4):
) )
stack.append(info) stack.append(info)
elif (token.kind in ('html_end', 'handlebars_end', 'html_singleton_end', elif (token.kind in ('html_end', 'handlebars_end', 'html_singleton_end',
'django_end', 'handlebars_singleton_end') and 'django_end', 'handlebars_singleton_end',
'jinja2_whitespace_stripped_end') and
(stack[-1]['tag'] != 'pre' or token.tag == 'pre')): (stack[-1]['tag'] != 'pre' or token.tag == 'pre')):
info = stack.pop() info = stack.pop()
if info['block']: if info['block']:

View File

@ -87,6 +87,15 @@ def tokenize(text):
# type: () -> bool # type: () -> bool
return looking_at("{% end") return looking_at("{% end")
def looking_at_jinja2_end_whitespace_stripped():
# type: () -> bool
return looking_at("{%- end")
def looking_at_jinja2_start_whitespace_stripped_type2():
# type: () -> bool
# This function detects tag like {%- if foo -%}...{% endif %}
return looking_at("{%-") and not looking_at("{%- end")
state = TokenizerState() state = TokenizerState()
tokens = [] tokens = []
@ -139,10 +148,21 @@ def tokenize(text):
s = get_django_tag(text, state.i) s = get_django_tag(text, state.i)
tag = s[3:-2].split()[0] tag = s[3:-2].split()[0]
kind = 'django_start' kind = 'django_start'
if s[-3] == '-':
kind = 'jinja2_whitespace_stripped_start'
elif looking_at_django_end(): elif looking_at_django_end():
s = get_django_tag(text, state.i) s = get_django_tag(text, state.i)
tag = s[6:-3] tag = s[6:-3]
kind = 'django_end' kind = 'django_end'
elif looking_at_jinja2_end_whitespace_stripped():
s = get_django_tag(text, state.i)
tag = s[7:-3]
kind = 'jinja2_whitespace_stripped_end'
elif looking_at_jinja2_start_whitespace_stripped_type2():
s = get_jinja2_whitespace_stripped_tag(text, state.i)
tag = s[3:-3].split()[0]
kind = 'jinja2_whitespace_stripped_type2_start'
else: else:
advance(1) advance(1)
continue continue
@ -274,7 +294,8 @@ def validate(fn=None, text=None, check_indent=True):
elif kind == 'handlebars_end': elif kind == 'handlebars_end':
state.matcher(token) state.matcher(token)
elif kind == 'django_start': elif kind in {'django_start',
'jinja2_whitespace_stripped_type2_start'}:
if is_django_block_tag(tag): if is_django_block_tag(tag):
start_tag_matcher(token) start_tag_matcher(token)
elif kind == 'django_end': elif kind == 'django_end':
@ -341,6 +362,16 @@ def get_django_tag(text, i):
s = text[i:end+2] s = text[i:end+2]
return s return s
def get_jinja2_whitespace_stripped_tag(text, i):
# type: (str, int) -> str
end = i + 3
while end < len(text) - 1 and text[end] != '%':
end += 1
if text[end-1] != '-' or text[end] != '%' or text[end+1] != '}':
raise TokenizationException('Tag missing "-%}"', text[i:end+2])
s = text[i:end+2]
return s
def get_html_tag(text, i): def get_html_tag(text, i):
# type: (str, int) -> str # type: (str, int) -> str
quote_count = 0 quote_count = 0

View File

@ -191,6 +191,54 @@ class ParserTest(unittest.TestCase):
''' '''
validate(text=my_html) validate(text=my_html)
def test_validate_jinja2_whitespace_markers(self) -> None:
my_html = '''
{% if foo -%}
this is foo
{%- endif %}
'''
validate(text=my_html)
def test_validate_mismatch_jinja2_whitespace_markers_1(self) -> None:
my_html = '''
{% if foo %}
this is foo
{%- endif %}
'''
self._assert_validate_error('Missing end tag', text=my_html)
def test_validate_mismatch_jinja2_whitespace_markers_2(self) -> None:
my_html = '''
{% if foo -%}
this is foo
{% endif %}
'''
self._assert_validate_error('No start tag', text=my_html)
def test_validate_jinja2_whitespace_type2_markers(self) -> None:
my_html = '''
{%- if foo -%}
this is foo
{% endif %}
'''
validate(text=my_html)
def test_validate_mismatch_jinja2_whitespace_type2_markers(self) -> None:
my_html = '''
{%- if foo -%}
this is foo
{%- endif %}
'''
self._assert_validate_error('Missing end tag', text=my_html)
def test_validate_incomplete_jinja2_whitespace_type2_markers(self) -> None:
my_html = '''
{%- if foo %}
this is foo
{% endif %}
'''
self._assert_validate_error('Tag missing "-%}" at Line 2 Col 9:"{%- if foo %}"', text=my_html)
def test_tokenize(self) -> None: def test_tokenize(self) -> None:
tag = '<meta whatever>bla' tag = '<meta whatever>bla'
token = tokenize(tag)[0] token = tokenize(tag)[0]
@ -240,3 +288,18 @@ class ParserTest(unittest.TestCase):
token = tokenize(tag)[0] token = tokenize(tag)[0]
self.assertEqual(token.kind, 'django_end') self.assertEqual(token.kind, 'django_end')
self.assertEqual(token.tag, 'if') self.assertEqual(token.tag, 'if')
tag = '{% if foo -%}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_start')
self.assertEqual(token.tag, 'if')
tag = '{%- endif %}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_end')
self.assertEqual(token.tag, 'if')
tag = '{%- if foo -%}bla'
token = tokenize(tag)[0]
self.assertEqual(token.kind, 'jinja2_whitespace_stripped_type2_start')
self.assertEqual(token.tag, 'if')