filter tag works now
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 83e07e6..e291f1d 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -113,6 +113,7 @@
# situations.
self.rootlevel = False
self.parent = parent
+ self.buffer = None
self.block = parent and parent.block or None
if parent is not None:
self.identifiers.declared.update(
@@ -120,11 +121,12 @@
parent.identifiers.declared_locally |
parent.identifiers.declared_parameter
)
+ self.buffer = parent.buffer
def copy(self):
"""Create a copy of the current one."""
rv = copy(self)
- rv.identifiers = copy(self)
+ rv.identifiers = copy(self.identifiers)
return rv
def inspect(self, nodes, hard_scope=False):
@@ -229,7 +231,7 @@
def blockvisit(self, nodes, frame, force_generator=False):
self.indent()
- if force_generator:
+ if force_generator and frame.buffer is None:
self.writeline('if 0: yield None')
try:
for node in nodes:
@@ -296,6 +298,16 @@
if not no_indent:
self.outdent()
+ def collect_shadowed(self, frame):
+ # make sure we "backup" overridden, local identifiers
+ # TODO: we should probably optimize this and check if the
+ # identifier is in use afterwards.
+ aliases = {}
+ for name in frame.identifiers.find_shadowed():
+ aliases[name] = ident = self.temporary_identifier()
+ self.writeline('%s = l_%s' % (ident, name))
+ return aliases
+
def function_scoping(self, node, frame):
func_frame = frame.inner()
func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
@@ -417,7 +429,10 @@
level += 1
self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
self.indent()
- self.writeline('yield event')
+ if frame.buffer is None:
+ self.writeline('yield event')
+ else:
+ self.writeline('%s.append(event)' % frame.buffer)
self.outdent(level)
def visit_Extends(self, node, frame):
@@ -479,7 +494,10 @@
self.writeline('included_context = included_stream.next()')
self.writeline('for event in included_stream:')
self.indent()
- self.writeline('yield event')
+ if frame.buffer is None:
+ self.writeline('yield event')
+ else:
+ self.writeline('%s.append(event)' % frame.buffer)
self.outdent()
# if we have a toplevel include the exported variables are copied
@@ -493,16 +511,10 @@
loop_frame.inspect(node.iter_child_nodes())
extended_loop = bool(node.else_) or \
'loop' in loop_frame.identifiers.undeclared
- loop_frame.identifiers.add_special('loop')
+ if extended_loop:
+ loop_frame.identifiers.add_special('loop')
- # make sure we "backup" overridden, local identifiers
- # TODO: we should probably optimize this and check if the
- # identifier is in use afterwards.
- aliases = {}
- for name in loop_frame.identifiers.find_shadowed():
- aliases[name] = ident = self.temporary_identifier()
- self.writeline('%s = l_%s' % (ident, name))
-
+ aliases = self.collect_shadowed(loop_frame)
self.pull_locals(loop_frame, True)
self.newline(node)
@@ -570,8 +582,33 @@
self.visit(arg)
self.write(', ')
self.write('), %r, False)' % call_frame.accesses_arguments)
- self.writeline('yield ', node)
+ if frame.buffer is None:
+ self.writeline('yield ', node)
+ else:
+ self.writeline('%s.append(' % frame.buffer, node)
self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
+ if frame.buffer is not None:
+ self.write(')')
+
+ def visit_FilterBlock(self, node, frame):
+ filter_frame = frame.inner()
+ filter_frame.inspect(node.iter_child_nodes())
+
+ aliases = self.collect_shadowed(filter_frame)
+ self.pull_locals(filter_frame, True)
+ filter_frame.buffer = buf = self.temporary_identifier()
+
+ self.writeline('%s = []' % buf, node)
+ for child in node.body:
+ self.visit(child, filter_frame)
+
+ if frame.buffer is None:
+ self.writeline('yield ', node)
+ else:
+ self.writeline('%s.append(' % frame.buffer, node)
+ self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
+ if frame.buffer is not None:
+ self.write(')')
def visit_ExprStmt(self, node, frame):
self.newline(node)
@@ -617,12 +654,20 @@
if len(body) < 3:
for item in body:
if isinstance(item, list):
- self.writeline('yield %s' % repr(u''.join(item)))
+ val = repr(u''.join(item))
+ if frame.buffer is None:
+ self.writeline('yield ' + val)
+ else:
+ self.writeline('%s.append(%s)' % (frame.buffer, val))
else:
self.newline(item)
- self.write('yield %s(' % finalizer)
+ if frame.buffer is None:
+ self.write('yield ')
+ else:
+ self.write('%s.append(' % frame.buffer)
+ self.write(finalizer + '(')
self.visit(item, frame)
- self.write(')')
+ self.write(')' * (1 + frame.buffer is not None))
# otherwise we create a format string as this is faster in that case
else:
@@ -634,7 +679,11 @@
else:
format.append('%s')
arguments.append(item)
- self.writeline('yield %r %% (' % u''.join(format))
+ if frame.buffer is None:
+ self.writeline('yield ')
+ else:
+ self.writeline('%s.append(' % frame.buffer)
+ self.write(repr(u''.join(format)) + ' % (')
idx = -1
for idx, argument in enumerate(arguments):
if idx:
@@ -645,6 +694,8 @@
if have_finalizer:
self.write(')')
self.write(idx == 0 and ',)' or ')')
+ if frame.buffer is not None:
+ self.write(')')
if outdent_later:
self.outdent()
@@ -784,12 +835,15 @@
self.write(':')
self.visit(node.step, frame)
- def visit_Filter(self, node, frame):
+ def visit_Filter(self, node, frame, initial=None):
self.write('f_%s(' % node.name)
- func = self.environment.filters.get(node.name)
- if getattr(func, 'contextfilter', False):
- self.write('context, ')
- self.visit(node.node, frame)
+ if initial is not None:
+ self.write(initial)
+ else:
+ func = self.environment.filters.get(node.name)
+ if getattr(func, 'contextfilter', False):
+ self.write('context, ')
+ self.visit(node.node, frame)
self.signature(node, frame)
self.write(')')
diff --git a/jinja2/environment.py b/jinja2/environment.py
index 8458a23..fb8101b 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -107,6 +107,7 @@
source = self.parse(source, filename)
node = optimize(source, self)
source = generate(node, self, filename)
+ print source
if raw:
return source
if isinstance(filename, unicode):
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index b569a31..3b0ac3c 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -232,7 +232,7 @@
class FilterBlock(Stmt):
"""Node for filter sections."""
- fields = ('body', 'filters')
+ fields = ('body', 'filter')
class Block(Stmt):
@@ -390,6 +390,8 @@
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
def as_const(self):
+ if self.node is None:
+ raise Impossible()
filter = self.environment.filters.get(self.name)
if filter is None or getattr(filter, 'contextfilter', False):
raise nodes.Impossible()
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 81c21ee..2d04211 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -52,6 +52,8 @@
return getattr(self, 'parse_' + token_type)()
elif token_type is 'call':
return self.parse_call_block()
+ elif token_type is 'filter':
+ return self.parse_filter_block()
lineno = self.stream.current
expr = self.parse_tuple()
if self.stream.current.type == 'assign':
@@ -206,6 +208,12 @@
node.body = self.parse_statements(('endcall',), drop_needle=True)
return node
+ def parse_filter_block(self):
+ node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
+ node.filter = self.parse_filter(None, start_inline=True)
+ node.body = self.parse_statements(('endfilter',), drop_needle=True)
+ return node
+
def parse_macro(self):
node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
node.name = self.stream.expect('name').value
@@ -581,10 +589,11 @@
return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
lineno=token.lineno)
- def parse_filter(self, node):
+ def parse_filter(self, node, start_inline=False):
lineno = self.stream.current.type
- while self.stream.current.type == 'pipe':
- self.stream.next()
+ while self.stream.current.type == 'pipe' or start_inline:
+ if not start_inline:
+ self.stream.next()
token = self.stream.expect('name')
if self.stream.current.type is 'lparen':
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
@@ -594,6 +603,7 @@
dyn_args = dyn_kwargs = None
node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
dyn_kwargs, lineno=token.lineno)
+ start_inline = False
return node
def parse_test(self, node):
@@ -634,12 +644,13 @@
while self.stream:
token = self.stream.current
if token.type is 'data':
- add_data(nodes.Const(token.value, lineno=token.lineno))
+ if token.value:
+ add_data(nodes.Const(token.value, lineno=token.lineno))
self.stream.next()
elif token.type is 'variable_begin':
self.stream.next()
want_comma = False
- while not self.stream.current.type in _statement_end_tokens:
+ while self.stream.current.type not in _statement_end_tokens:
if want_comma:
self.stream.expect('comma')
add_data(self.parse_expression())
diff --git a/test_filter_and_linestatements.py b/test_filter_and_linestatements.py
new file mode 100644
index 0000000..e5d94d0
--- /dev/null
+++ b/test_filter_and_linestatements.py
@@ -0,0 +1,22 @@
+from jinja2 import Environment
+
+
+env = Environment(line_statement_prefix='%', variable_start_string="${", variable_end_string="}")
+tmpl = env.from_string("""\
+% macro foo()
+ ${caller(42)}
+% endmacro
+<ul>
+% for item in seq
+ <li>${item}</li>
+% endfor
+</ul>
+% call(var) foo()
+ [${var}]
+% endcall
+% filter escape
+ <hello world>
+% endfilter
+""")
+
+print tmpl.render(seq=range(10))