Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1071)

Side by Side Diff: third_party/handlebar/handlebar.py

Issue 10857042: Extensions Docs Server: Faster Handlebar (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/handlebar/README.chromium ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2012 Benjamin Kalman 1 # Copyright 2012 Benjamin Kalman
2 # 2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); 3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License. 4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at 5 # You may obtain a copy of the License at
6 # 6 #
7 # http://www.apache.org/licenses/LICENSE-2.0 7 # http://www.apache.org/licenses/LICENSE-2.0
8 # 8 #
9 # Unless required by applicable law or agreed to in writing, software 9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, 10 # distributed under the License is distributed on an "AS IS" BASIS,
(...skipping 25 matching lines...) Expand all
36 a get() method. 36 a get() method.
37 37
38 class CustomContext(object): 38 class CustomContext(object):
39 def get(self, key): 39 def get(self, key):
40 return 10 40 return 10
41 41
42 # Any time {{ }} is used, will fill it with 10. 42 # Any time {{ }} is used, will fill it with 10.
43 print(Handlebar('hello {{world}}').render(CustomContext()).text) 43 print(Handlebar('hello {{world}}').render(CustomContext()).text)
44 """ 44 """
45 45
46 def _SafeStr(obj):
47 return obj if isinstance(obj, basestring) else str(obj)
48
49 class ParseException(Exception): 46 class ParseException(Exception):
50 """ Exception thrown while parsing the template. 47 """ Exception thrown while parsing the template.
51 """ 48 """
52 def __init__(self, error, line): 49 def __init__(self, error, line):
53 Exception.__init__(self, "%s (line %s)" % (error, line.number)) 50 Exception.__init__(self, "%s (line %s)" % (error, line.number))
54 51
55 class RenderResult(object): 52 class RenderResult(object):
56 """ Result of a render operation. 53 """ Result of a render operation.
57 """ 54 """
58 def __init__(self, text, errors): 55 def __init__(self, text, errors):
59 self.text = text; 56 self.text = text;
60 self.errors = errors 57 self.errors = errors
61 58
62 class StringBuilder(object): 59 class StringBuilder(object):
63 """ Mimics Java's StringBuilder for easy porting from the Java version of 60 """ Mimics Java's StringBuilder for easy porting from the Java version of
64 this file to Python. 61 this file to Python.
65 """ 62 """
66 def __init__(self): 63 def __init__(self):
67 self._buf = [] 64 self._buf = []
68 self._length = 0
69 65
70 def __len__(self): 66 def __len__(self):
71 return self._length 67 self._Collapse()
68 return len(self._buf[0])
72 69
73 def append(self, obj): 70 def append(self, string):
74 string = _SafeStr(obj)
75 self._buf.append(string) 71 self._buf.append(string)
76 self._length += len(string)
77 72
78 def toString(self): 73 def toString(self):
79 return u''.join(self._buf) 74 self._Collapse()
75 return self._buf[0]
80 76
81 def __str__(self): 77 def __str__(self):
82 return self.toString() 78 return self.toString()
83 79
80 def _Collapse(self):
81 self._buf = [u''.join(self._buf)]
82
84 class RenderState(object): 83 class RenderState(object):
85 """ The state of a render call. 84 """ The state of a render call.
86 """ 85 """
87 def __init__(self, globalContexts, localContexts): 86 def __init__(self, globalContexts, localContexts):
88 self.globalContexts = globalContexts 87 self.globalContexts = globalContexts
89 self.localContexts = localContexts 88 self.localContexts = localContexts
90 self.text = StringBuilder() 89 self.text = StringBuilder()
91 self.errors = [] 90 self.errors = []
92 self._errorsDisabled = False 91 self._errorsDisabled = False
93 92
94 def inSameContext(self): 93 def inSameContext(self):
95 return RenderState(self.globalContexts, self.localContexts) 94 return RenderState(self.globalContexts, self.localContexts)
96 95
97 def getFirstContext(self): 96 def getFirstContext(self):
98 if len(self.localContexts) > 0: 97 if len(self.localContexts) > 0:
99 return self.localContexts[0] 98 return self.localContexts[0]
100 if len(self.globalContexts) > 0: 99 if len(self.globalContexts) > 0:
101 return self.globalContexts[0] 100 return self.globalContexts[0]
102 return None 101 return None
103 102
104 def disableErrors(self): 103 def disableErrors(self):
105 self._errorsDisabled = True 104 self._errorsDisabled = True
106 return self 105 return self
107 106
108 def addError(self, *messages): 107 def addError(self, *messages):
109 if self._errorsDisabled: 108 if self._errorsDisabled:
110 return self 109 return self
111 buf = StringBuilder() 110 buf = StringBuilder()
112 for message in messages: 111 for message in messages:
113 buf.append(message) 112 buf.append(str(message))
114 self.errors.append(buf.toString()) 113 self.errors.append(buf.toString())
115 return self 114 return self
116 115
117 def getResult(self): 116 def getResult(self):
118 return RenderResult(self.text.toString(), self.errors); 117 return RenderResult(self.text.toString(), self.errors);
119 118
120 class Identifier(object): 119 class Identifier(object):
121 """ An identifier of the form "@", "foo.bar.baz", or "@.foo.bar.baz". 120 """ An identifier of the form "@", "foo.bar.baz", or "@.foo.bar.baz".
122 """ 121 """
123 def __init__(self, name, line): 122 def __init__(self, name, line):
(...skipping 13 matching lines...) Expand all
137 self._path = name.split('.') 136 self._path = name.split('.')
138 137
139 def resolve(self, renderState): 138 def resolve(self, renderState):
140 if self._isThis: 139 if self._isThis:
141 return renderState.getFirstContext() 140 return renderState.getFirstContext()
142 141
143 if self._startsWithThis: 142 if self._startsWithThis:
144 return self._resolveFromContext(renderState.getFirstContext()) 143 return self._resolveFromContext(renderState.getFirstContext())
145 144
146 resolved = self._resolveFromContexts(renderState.localContexts) 145 resolved = self._resolveFromContexts(renderState.localContexts)
147 if resolved == None: 146 if resolved is None:
148 resolved = self._resolveFromContexts(renderState.globalContexts) 147 resolved = self._resolveFromContexts(renderState.globalContexts)
149 if resolved == None: 148 if resolved is None:
150 renderState.addError("Couldn't resolve identifier ", self._path) 149 renderState.addError("Couldn't resolve identifier ", self._path)
151 return resolved 150 return resolved
152 151
153 def _resolveFromContexts(self, contexts): 152 def _resolveFromContexts(self, contexts):
154 for context in contexts: 153 for context in contexts:
155 resolved = self._resolveFromContext(context) 154 resolved = self._resolveFromContext(context)
156 if resolved != None: 155 if resolved is not None:
157 return resolved 156 return resolved
158 return None 157 return None
159 158
160 def _resolveFromContext(self, context): 159 def _resolveFromContext(self, context):
161 result = context 160 result = context
162 for next in self._path: 161 for next in self._path:
163 # Only require that contexts provide a get method, meaning that callers 162 # Only require that contexts provide a get method, meaning that callers
164 # can provide dict-like contexts (for example, to populate values lazily). 163 # can provide dict-like contexts (for example, to populate values lazily).
165 if result == None or not getattr(result, "get", None): 164 if result is None or not getattr(result, "get", None):
166 return None 165 return None
167 result = result.get(next) 166 result = result.get(next)
168 return result 167 return result
169 168
170 def __str__(self): 169 def __str__(self):
171 if self._isThis: 170 if self._isThis:
172 return '@' 171 return '@'
173 name = '.'.join(self._path) 172 name = '.'.join(self._path)
174 return ('@.' + name) if self._startsWithThis else name 173 return ('@.' + name) if self._startsWithThis else name
175 174
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 228
230 class InlineNode(DecoratorNode): 229 class InlineNode(DecoratorNode):
231 def __init__(self, content): 230 def __init__(self, content):
232 DecoratorNode.__init__(self, content) 231 DecoratorNode.__init__(self, content)
233 232
234 def render(self, renderState): 233 def render(self, renderState):
235 contentRenderState = renderState.inSameContext() 234 contentRenderState = renderState.inSameContext()
236 self._content.render(contentRenderState) 235 self._content.render(contentRenderState)
237 236
238 renderState.errors.extend(contentRenderState.errors) 237 renderState.errors.extend(contentRenderState.errors)
239 238 renderState.text.append(
240 for c in contentRenderState.text.toString(): 239 contentRenderState.text.toString().replace('\n', ''))
241 if c != '\n':
242 renderState.text.append(c)
243 240
244 class IndentedNode(DecoratorNode): 241 class IndentedNode(DecoratorNode):
245 def __init__(self, content, indentation): 242 def __init__(self, content, indentation):
246 DecoratorNode.__init__(self, content) 243 DecoratorNode.__init__(self, content)
247 self._indentation = indentation 244 self._indent_str = ' ' * indentation
248 245
249 def render(self, renderState): 246 def render(self, renderState):
250 contentRenderState = renderState.inSameContext() 247 contentRenderState = renderState.inSameContext()
251 self._content.render(contentRenderState) 248 self._content.render(contentRenderState)
252 249
253 renderState.errors.extend(contentRenderState.errors) 250 renderState.errors.extend(contentRenderState.errors)
254 251 renderState.text.append(self._indent_str)
255 self._indent(renderState.text) 252 # TODO: this might introduce an extra \n at the end? need test.
256 for i, c in enumerate(contentRenderState.text.toString()): 253 renderState.text.append(
257 renderState.text.append(c) 254 contentRenderState.text.toString().replace('\n',
258 if c == '\n' and i < len(contentRenderState.text) - 1: 255 '\n' + self._indent_str))
259 self._indent(renderState.text)
260 renderState.text.append('\n') 256 renderState.text.append('\n')
261 257
262 def _indent(self, buf):
263 buf.append(' ' * self._indentation)
264
265 class BlockNode(DecoratorNode): 258 class BlockNode(DecoratorNode):
266 def __init__(self, content): 259 def __init__(self, content):
267 DecoratorNode.__init__(self, content) 260 DecoratorNode.__init__(self, content)
268 content.trimStartingNewLine() 261 content.trimStartingNewLine()
269 content.trimEndingSpaces() 262 content.trimEndingSpaces()
270 263
271 def render(self, renderState): 264 def render(self, renderState):
272 self._content.render(renderState) 265 self._content.render(renderState)
273 266
274 class NodeCollection(object): 267 class NodeCollection(object):
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 340
348 class EscapedVariableNode(LeafNode): 341 class EscapedVariableNode(LeafNode):
349 """ {{foo}} 342 """ {{foo}}
350 """ 343 """
351 def __init__(self, id, line): 344 def __init__(self, id, line):
352 LeafNode.__init__(self, line) 345 LeafNode.__init__(self, line)
353 self._id = id 346 self._id = id
354 347
355 def render(self, renderState): 348 def render(self, renderState):
356 value = self._id.resolve(renderState) 349 value = self._id.resolve(renderState)
357 if value != None: 350 if value is None:
358 self._appendEscapedHtml(renderState.text, _SafeStr(value)) 351 return
359 352
360 def _appendEscapedHtml(self, escaped, unescaped): 353 string = value if isinstance(value, basestring) else str(value)
361 for c in unescaped: 354 renderState.text.append(string.replace('&', '&amp;')
362 if c == '<': 355 .replace('<', '&lt;')
363 escaped.append("&lt;") 356 .replace('>', '&gt;'))
364 elif c == '>':
365 escaped.append("&gt;")
366 elif c == '&':
367 escaped.append("&amp;")
368 else:
369 escaped.append(c)
370 357
371 class UnescapedVariableNode(LeafNode): 358 class UnescapedVariableNode(LeafNode):
372 """ {{{foo}}} 359 """ {{{foo}}}
373 """ 360 """
374 def __init__(self, id, line): 361 def __init__(self, id, line):
375 LeafNode.__init__(self, line) 362 LeafNode.__init__(self, line)
376 self._id = id 363 self._id = id
377 364
378 def render(self, renderState): 365 def render(self, renderState):
379 value = self._id.resolve(renderState) 366 value = self._id.resolve(renderState)
380 if value != None: 367 if value is None:
381 renderState.text.append(value) 368 return
369 renderState.text.append(
370 value if isinstance(value, basestring) else str(value))
382 371
383 class SectionNode(DecoratorNode): 372 class SectionNode(DecoratorNode):
384 """ {{#foo}} ... {{/}} 373 """ {{#foo}} ... {{/}}
385 """ 374 """
386 def __init__(self, id, content): 375 def __init__(self, id, content):
387 DecoratorNode.__init__(self, content) 376 DecoratorNode.__init__(self, content)
388 self._id = id 377 self._id = id
389 378
390 def render(self, renderState): 379 def render(self, renderState):
391 value = self._id.resolve(renderState) 380 value = self._id.resolve(renderState)
392 if value == None: 381 if value is None:
393 return 382 return
394 383
395 if isinstance(value, list): 384 if isinstance(value, list):
396 for item in value: 385 for item in value:
397 renderState.localContexts.insert(0, item) 386 renderState.localContexts.insert(0, item)
398 self._content.render(renderState) 387 self._content.render(renderState)
399 renderState.localContexts.pop(0) 388 renderState.localContexts.pop(0)
400 elif isinstance(value, dict): 389 elif isinstance(value, dict):
401 renderState.localContexts.insert(0, value) 390 renderState.localContexts.insert(0, value)
402 self._content.render(renderState) 391 self._content.render(renderState)
(...skipping 10 matching lines...) Expand all
413 self._id = id 402 self._id = id
414 403
415 def render(self, renderState): 404 def render(self, renderState):
416 value = self._id.resolve(renderState.inSameContext().disableErrors()) 405 value = self._id.resolve(renderState.inSameContext().disableErrors())
417 if _VertedSectionNodeShouldRender(value): 406 if _VertedSectionNodeShouldRender(value):
418 renderState.localContexts.insert(0, value) 407 renderState.localContexts.insert(0, value)
419 self._content.render(renderState) 408 self._content.render(renderState)
420 renderState.localContexts.pop(0) 409 renderState.localContexts.pop(0)
421 410
422 def _VertedSectionNodeShouldRender(value): 411 def _VertedSectionNodeShouldRender(value):
423 if value == None: 412 if value is None:
424 return False 413 return False
425 if isinstance(value, bool): 414 if isinstance(value, bool):
426 return value 415 return value
427 if (isinstance(value, int) or 416 if (isinstance(value, int) or
428 isinstance(value, long) or 417 isinstance(value, long) or
429 isinstance(value, float)): 418 isinstance(value, float)):
430 return True 419 return True
431 if isinstance(value, basestring): 420 if isinstance(value, basestring):
432 return True 421 return True
433 if isinstance(value, list): 422 if isinstance(value, list):
(...skipping 16 matching lines...) Expand all
450 439
451 class JsonNode(LeafNode): 440 class JsonNode(LeafNode):
452 """ {{*foo}} 441 """ {{*foo}}
453 """ 442 """
454 def __init__(self, id, line): 443 def __init__(self, id, line):
455 LeafNode.__init__(self, line) 444 LeafNode.__init__(self, line)
456 self._id = id 445 self._id = id
457 446
458 def render(self, renderState): 447 def render(self, renderState):
459 value = self._id.resolve(renderState) 448 value = self._id.resolve(renderState)
460 if value != None: 449 if value is None:
461 renderState.text.append(json.dumps(value, separators=(',',':'))) 450 return
451 renderState.text.append(json.dumps(value, separators=(',',':')))
462 452
463 class PartialNode(LeafNode): 453 class PartialNode(LeafNode):
464 """ {{+foo}} 454 """ {{+foo}}
465 """ 455 """
466 def __init__(self, id, line): 456 def __init__(self, id, line):
467 LeafNode.__init__(self, line) 457 LeafNode.__init__(self, line)
468 self._id = id 458 self._id = id
469 self._args = None 459 self._args = None
470 460
471 def render(self, renderState): 461 def render(self, renderState):
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 """ 532 """
543 def __init__(self, string): 533 def __init__(self, string):
544 self._remainder = string 534 self._remainder = string
545 535
546 self.nextToken = None 536 self.nextToken = None
547 self.nextContents = None 537 self.nextContents = None
548 self.nextLine = Line(1) 538 self.nextLine = Line(1)
549 self.advance() 539 self.advance()
550 540
551 def hasNext(self): 541 def hasNext(self):
552 return self.nextToken != None 542 return self.nextToken is not None
553 543
554 def advance(self): 544 def advance(self):
555 if self.nextContents == '\n': 545 if self.nextContents == '\n':
556 self.nextLine = Line(self.nextLine.number + 1) 546 self.nextLine = Line(self.nextLine.number + 1)
557 547
558 self.nextToken = None 548 self.nextToken = None
559 self.nextContents = None 549 self.nextContents = None
560 550
561 if self._remainder == '': 551 if self._remainder == '':
562 return None 552 return None
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
747 def render(self, *contexts): 737 def render(self, *contexts):
748 """ Renders this template given a variable number of "contexts" to read 738 """ Renders this template given a variable number of "contexts" to read
749 out values from (such as those appearing in {{foo}}). 739 out values from (such as those appearing in {{foo}}).
750 """ 740 """
751 globalContexts = [] 741 globalContexts = []
752 for context in contexts: 742 for context in contexts:
753 globalContexts.append(context) 743 globalContexts.append(context)
754 renderState = RenderState(globalContexts, []) 744 renderState = RenderState(globalContexts, [])
755 self._topNode.render(renderState) 745 self._topNode.render(renderState)
756 return renderState.getResult() 746 return renderState.getResult()
OLDNEW
« no previous file with comments | « third_party/handlebar/README.chromium ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698