forked from zulip/zulip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck-templates
executable file
·186 lines (156 loc) · 4.99 KB
/
check-templates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env python
import os
import sys
import subprocess
class Record:
pass
def validate(fn, check_indent=True):
text = open(fn).read()
state = Record()
def NoStartTag(end_tag):
raise Exception('''
No start tag
fn: %s
end tag:
%s
line %d, col %d
''' % (fn, end_tag, state.line, state.col))
def start_tag_matcher(s, start_tag):
start_line = state.line
start_col = state.col
state.depth += 1
old_matcher = state.matcher
def f(end_tag):
problem = None
if start_tag != end_tag:
problem = 'Mismatched tag.'
elif check_indent and state.line > start_line + 1 and state.col != start_col:
problem = 'Bad indentation.'
if problem:
raise Exception('''
fn: %s
%s
start:
%s
line %d, col %d
end tag:
%s
line %d, col %d
''' % (fn, problem, s, start_line, start_col, end_tag, state.line, state.col))
state.matcher = old_matcher
state.depth -= 1
state.matcher = f
state.depth = 0
state.i = 0
state.line = 1
state.col = 1
state.matcher = NoStartTag
def advance(n):
for _ in range(n):
state.i += 1
if state.i >= 0 and text[state.i - 1] == '\n':
state.line += 1
state.col = 1
else:
state.col += 1
def looking_at(s):
return text[state.i:state.i+len(s)] == s
while state.i < len(text):
# HTML tags
if looking_at("<") and not looking_at("</"):
s = get_html_tag(text, state.i)
tag = s[1:-1].split()[0]
ignore = s.startswith('<!--') or s.endswith('/>') or tag in ['meta', '!DOCTYPE']
if not ignore:
start_tag_matcher(s, tag)
advance(len(s))
continue
if looking_at("</"):
s = get_html_tag(text, state.i)
end_tag = s[2:-1]
state.matcher(end_tag)
advance(len(s))
continue
# Handlebar tags
if looking_at("{{#") or looking_at("{{^"):
s = get_handlebars_tag(text, state.i)
tag = s[3:-2].split()[0]
start_tag_matcher(s, tag)
advance(len(s))
continue
if looking_at("{{/"):
s = get_handlebars_tag(text, state.i)
end_tag = s[3:-2]
state.matcher(end_tag)
advance(len(s))
continue
# Django tags
if looking_at("{% ") and not looking_at("{% end"):
s = get_django_tag(text, state.i)
tag = s[3:-2].split()[0]
if is_django_block_tag(tag):
start_tag_matcher(s, tag)
advance(len(s))
continue
if looking_at("{% end"):
s = get_django_tag(text, state.i)
end_tag = s[6:-3]
state.matcher(end_tag)
advance(len(s))
continue
advance(1)
if state.depth != 0:
return state.matcher("(NO TAG)")
def is_django_block_tag(tag):
return tag in [
'autoescape',
'block',
'comment',
'for',
'if',
'ifequal',
'verbatim',
]
def get_handlebars_tag(text, i):
end = i + 2
while end < len(text) -1 and text[end] != '}':
end += 1
if text[end] != '}' or text[end+1] != '}':
raise Exception('Tag missing }}')
s = text[i:end+2]
return s
def get_django_tag(text, i):
end = i + 2
while end < len(text) -1 and text[end] != '%':
end += 1
if text[end] != '%' or text[end+1] != '}':
raise Exception('Tag missing %}')
s = text[i:end+2]
return s
def get_html_tag(text, i):
end = i + 1
while end < len(text) and text[end] != '>':
end += 1
if text[end] != '>':
raise Exception('Tag missing >')
s = text[i:end+1]
return s
def check_our_files():
git_files = map(str.strip, subprocess.check_output(['git', 'ls-files']).split('\n'))
# Check all our handlebars templates.
templates = [fn for fn in git_files if fn.endswith('.handlebars')]
assert len(templates) >= 10 # sanity check that we are actually doing work
for fn in templates:
validate(fn)
# Django templates are pretty messy now, so we do minimal checking.
templates = sorted([fn for fn in git_files if fn.endswith('.html') and 'templates' in fn])
def ok(fn):
if 'api.html' in fn: return False
if 'base.html' in fn: return False
return True
templates = filter(ok, templates)
assert len(templates) >= 10 # sanity check that we are actually doing work
for fn in templates:
validate(fn, check_indent=False)
if __name__ == '__main__':
check_our_files()