Add Django and html singleton tags support to pretty print.

In this commit we are modifying pretty print tool to support
Django and html singleton tags. For Addition of html singleton
tags template parser was modified to emit psudeo
html singleton end tags to accompany html singleton tags and
token class was updated to have line_span field.
This commit is contained in:
adnrs96 2017-02-17 00:55:53 +05:30 committed by Tim Abbott
parent 1458d0d3a2
commit 3acf8b050d
3 changed files with 107 additions and 34 deletions

View File

@ -38,39 +38,50 @@ def pretty_print_html(html, num_spaces=4):
# we proceed, we will push/pop info dictionaries on/off a stack.
for token in tokens:
if token.kind in ('html_start', 'handlebars_start'):
if token.kind in ('html_start', 'handlebars_start',
'html_singleton', 'django_start'):
# An HTML start tag should only cause a new indent if we
# are on a new line.
is_block = token.line > stack[-1]['line']
if token.tag not in ('extends', 'include', 'else', 'elif'):
is_block = token.line > stack[-1]['line']
if is_block:
if token.kind == 'handlebars_start' and stack[-1]['token_kind'] == 'handlebars_start':
info = stack.pop()
info['depth'] = info['depth'] + 1
stack.append(info)
new_depth = stack[-1]['depth'] + 1
extra_indent = stack[-1]['extra_indent']
line = lines[token.line - 1]
adjustment = len(line)-len(line.lstrip()) + 1
offset = (1 + extra_indent + new_depth * num_spaces) - adjustment
info = dict(
block=True,
depth=new_depth,
actual_depth=new_depth,
line=token.line,
token_kind=token.kind,
offset=offset,
extra_indent=token.col - adjustment + extra_indent
)
if token.kind == 'handlebars_start':
info.update(dict(depth=new_depth - 1))
else:
info = dict(
block=False,
line=token.line
)
stack.append(info)
elif token.kind in ('html_end', 'handlebars_end'):
if is_block:
if (token.kind == 'handlebars_start' and
stack[-1]['token_kind'] == 'handlebars_start' and
not stack[-1]['indenting']):
info = stack.pop()
info['depth'] = info['depth'] + 1
info['indenting'] = True
stack.append(info)
new_depth = stack[-1]['depth'] + 1
extra_indent = stack[-1]['extra_indent']
line = lines[token.line - 1]
adjustment = len(line)-len(line.lstrip()) + 1
offset = (1 + extra_indent + new_depth * num_spaces) - adjustment
info = dict(
block=True,
depth=new_depth,
actual_depth=new_depth,
line=token.line,
token_kind=token.kind,
offset=offset,
extra_indent=token.col - adjustment + extra_indent,
indenting=True
)
if token.kind == 'handlebars_start':
info.update(dict(depth=new_depth - 1, indenting=False))
else:
info = dict(
block=False,
depth=stack[-1]['depth'],
actual_depth=stack[-1]['depth'],
line=token.line,
token_kind=token.kind,
extra_indent=stack[-1]['extra_indent']
)
stack.append(info)
elif token.kind in ('html_end', 'handlebars_end',
'html_singleton_end', 'django_end'):
info = stack.pop()
if info['block']:
# We are at the end of an indentation block. We
@ -81,14 +92,16 @@ def pretty_print_html(html, num_spaces=4):
end_line = token.line
offsets[start_line] = info['offset']
offsets[end_line] = info['offset']
if token.tag != 'pre':
if token.tag != 'pre' and token.kind != 'html_singleton_end' and token.tag != 'script':
for line_num in range(start_line + 1, end_line):
# Be careful not to override offsets that happened
# deeper in the HTML within our block.
if line_num not in offsets:
line = lines[line_num - 1]
new_depth = info['depth'] + 1
if line.lstrip().startswith('{{else}}'):
if (line.lstrip().startswith('{{else}}') or
line.lstrip().startswith('{% else %}') or
line.lstrip().startswith('{% elif')):
new_depth = info['actual_depth']
extra_indent = info['extra_indent']
adjustment = len(line)-len(line.lstrip()) + 1

View File

@ -27,13 +27,14 @@ class TokenizerState(object):
self.col = 1
class Token(object):
def __init__(self, kind, s, tag, line, col):
# type: (str, str, str, int, int) -> None
def __init__(self, kind, s, tag, line, col, line_span):
# type: (str, str, str, int, int, int) -> None
self.kind = kind
self.s = s
self.tag = tag
self.line = line
self.col = col
self.line_span = line_span
def tokenize(text):
# type: (str) -> List[Token]
@ -131,15 +132,30 @@ def tokenize(text):
(e.message, state.line, state.col,
e.line_content))
line_span = len(s.split('\n'))
token = Token(
kind=kind,
s=s,
tag=tag,
line=state.line,
col=state.col,
line_span=line_span
)
tokens.append(token)
advance(len(s))
if kind == 'html_singleton':
# Here we insert a Pseudo html_singleton_end tag so as to have
# ease of detection of end of singleton html tags which might be
# needed in some cases as with our html pretty printer.
token = Token(
kind='html_singleton_end',
s='</' + tag + '>',
tag=tag,
line=state.line,
col=state.col,
line_span=1
)
tokens.append(token)
return tokens

View File

@ -188,6 +188,48 @@ GOOD_HTML6 = """
<p> <strong> <span class = "whatever">foobar </span> </strong></p>
</div>
"""
BAD_HTML7 = """
<div class="foobar">
<input type="foobar" name="temp" value="{{dyn_name}}"
{{#unless invite_only}}checked="checked"{{/unless}} /> {{dyn_name}}
{{#if invite_only}}<i class="icon-vector-lock"></i>{{/if}}
</div>
"""
GOOD_HTML7 = """
<div class="foobar">
<input type="foobar" name="temp" value="{{dyn_name}}"
{{#unless invite_only}}checked="checked"{{/unless}} /> {{dyn_name}}
{{#if invite_only}}<i class="icon-vector-lock"></i>{{/if}}
</div>
"""
BAD_HTML8 = """
{{#each test}}
{{#with this}}
{{#if foobar}}
<div class="anything">{{{test}}}</div>
{{/if}}
{{#if foobar2}}
{{partial "teststuff"}}
{{/if}}
{{/with}}
{{/each}}
"""
GOOD_HTML8 = """
{{#each test}}
{{#with this}}
{{#if foobar}}
<div class="anything">{{{test}}}</div>
{{/if}}
{{#if foobar2}}
{{partial "teststuff"}}
{{/if}}
{{/with}}
{{/each}}
"""
class TestPrettyPrinter(unittest.TestCase):
def compare(self, a, b):
# type: (str, str) -> None
@ -203,3 +245,5 @@ class TestPrettyPrinter(unittest.TestCase):
self.compare(pretty_print_html(BAD_HTML4), GOOD_HTML4)
self.compare(pretty_print_html(BAD_HTML5), GOOD_HTML5)
self.compare(pretty_print_html(BAD_HTML6), GOOD_HTML6)
self.compare(pretty_print_html(BAD_HTML7), GOOD_HTML7)
self.compare(pretty_print_html(BAD_HTML8), GOOD_HTML8)