diff options
| author | 2017-09-07 20:42:55 +0200 | |
|---|---|---|
| committer | 2017-09-07 20:42:55 +0200 | |
| commit | 36b06915177ccc8da1cdefa46fdde7ff44938ef8 (patch) | |
| tree | 4b524e287223e7be41323861436673767bb89215 /askama_shared/src/generator.rs | |
| parent | 34fcbf028aff02e02f0e9cfc9229a9f583d5bbad (diff) | |
| download | askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.tar.gz askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.tar.bz2 askama-36b06915177ccc8da1cdefa46fdde7ff44938ef8.zip | |
Reorder methods in Generator to aid readability
Diffstat (limited to 'askama_shared/src/generator.rs')
| -rw-r--r-- | askama_shared/src/generator.rs | 900 | 
1 files changed, 450 insertions, 450 deletions
| diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 9ac6964..9ccbbbd 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -126,295 +126,260 @@ impl<'a> Generator<'a> {          Self::new(locals, self.indent)      } -    /* Helper methods for writing to internal buffer */ - -    fn indent(&mut self) { -        self.indent += 1; -    } - -    fn dedent(&mut self) { -        self.indent -= 1; -    } - -    fn write(&mut self, s: &str) { -        if self.start { -            for _ in 0..(self.indent * 4) { -                self.buf.push(' '); +    fn result(mut self, state: &'a State) -> String { +        if !state.blocks.is_empty() { +            if !state.derived { +                self.define_trait(state); +            } else { +                let parent_type = get_parent_type(state.input.ast) +                    .expect("expected field '_parent' in extending template struct"); +                self.deref_to_parent(state, parent_type);              } -            self.start = false; -        } -        self.buf.push_str(s); -    } -    fn writeln(&mut self, s: &str) { -        if s.is_empty() { -            return; +            let trait_nodes = if !state.derived { Some(&state.nodes[..]) } else { None }; +            self.impl_trait(state, trait_nodes); +            self.impl_template_for_trait(state); +        } else { +            self.impl_template(state);          } -        if s == "}" { -            self.dedent(); +        self.impl_display(state); +        if cfg!(feature = "iron") { +            self.impl_modifier_response(state);          } -        self.write(s); -        if s.ends_with('{') { -            self.indent(); +        if cfg!(feature = "rocket") { +            self.impl_responder(state);          } -        self.buf.push('\n'); -        self.start = true; +        self.buf      } -    /* Helper methods for dealing with whitespace nodes */ - -    fn flush_ws(&mut self, ws: &WS) { -        if self.next_ws.is_some() && !ws.0 { -            let val = self.next_ws.unwrap(); -            if !val.is_empty() { -                self.writeln(&format!("writer.write_str({:#?})?;", -                                      val)); -            } -        } -        self.next_ws = None; +    // Implement `Template` for the given context struct. +    fn impl_template(&mut self, state: &'a State) { +        self.write_header(state, "::askama::Template", &[]); +        self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) -> \ +                      ::askama::Result<()> {"); +        self.handle(state, state.nodes, AstLevel::Top); +        self.flush_ws(&WS(false, false)); +        self.writeln("Ok(())"); +        self.writeln("}"); +        self.writeln("}");      } -    fn prepare_ws(&mut self, ws: &WS) { -        self.skip_ws = ws.1; +    // Implement `Display` for the given context struct. +    fn impl_display(&mut self, state: &'a State) { +        self.write_header(state, "::std::fmt::Display", &[]); +        self.writeln("fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {"); +        self.writeln("self.render_into(f).map_err(|_| ::std::fmt::Error {})"); +        self.writeln("}"); +        self.writeln("}");      } -    fn handle_ws(&mut self, ws: &WS) { -        self.flush_ws(ws); -        self.prepare_ws(ws); +    // Implement `Deref<Parent>` for an inheriting context struct. +    fn deref_to_parent(&mut self, state: &'a State, parent_type: &syn::Ty) { +        self.write_header(state, "::std::ops::Deref", &[]); +        let mut tokens = Tokens::new(); +        parent_type.to_tokens(&mut tokens); +        self.writeln(&format!("type Target = {};", tokens.as_str())); +        self.writeln("fn deref(&self) -> &Self::Target {"); +        self.writeln("&self._parent"); +        self.writeln("}"); +        self.writeln("}");      } -    /* Visitor methods for expression types */ - -    fn visit_num_lit(&mut self, s: &str) -> DisplayWrap { -        self.write(s); -        DisplayWrap::Unwrapped -    } +    // Implement `TraitFromPathName` for the given context struct. +    fn impl_trait(&mut self, state: &'a State, nodes: Option<&'a [Node]>) { +        self.write_header(state, &state.trait_name, &[]); +        self.write_block_defs(state); -    fn visit_str_lit(&mut self, s: &str) -> DisplayWrap { -        self.write(&format!("\"{}\"", s)); -        DisplayWrap::Unwrapped -    } +        self.writeln("#[allow(unused_variables)]"); +        self.writeln(&format!( +            "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ +             -> ::askama::Result<()> {{", +            state.trait_name)); -    fn visit_var(&mut self, s: &str) -> DisplayWrap { -        if self.locals.contains(s) { -            self.write(s); +        if let Some(nodes) = nodes { +            self.handle(state, nodes, AstLevel::Top); +            self.flush_ws(&WS(false, false));          } else { -            self.write(&format!("self.{}", s)); +            self.writeln("self._parent.render_trait_into(self, writer)?;");          } -        DisplayWrap::Unwrapped -    } -    fn visit_attr(&mut self, obj: &Expr, attr: &str) -> DisplayWrap { -        if let Expr::Var(name) = *obj { -            if name == "loop" { -                self.write("_loop_index"); -                if attr == "index" { -                    self.write(" + 1"); -                    return DisplayWrap::Unwrapped; -                } else if attr == "index0" { -                    return DisplayWrap::Unwrapped; -                } else { -                    panic!("unknown loop variable"); -                } -            } -        } -        self.visit_expr(obj); -        self.write(&format!(".{}", attr)); -        DisplayWrap::Unwrapped +        self.writeln("Ok(())"); +        self.writeln("}"); +        self.flush_ws(&WS(false, false)); +        self.writeln("}");      } -    fn _visit_filter_args(&mut self, args: &[Expr]) { -        for (i, arg) in args.iter().enumerate() { -            if i > 0 { -                self.write(", &"); -            } -            self.visit_expr(arg); +    // Implement `Template` for templates that implement a template trait. +    fn impl_template_for_trait(&mut self, state: &'a State) { +        self.write_header(state, "::askama::Template", &[]); +        self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) \ +                      -> ::askama::Result<()> {"); +        if state.derived { +            self.writeln("self._parent.render_trait_into(self, writer)?;"); +        } else { +            self.writeln("self.render_trait_into(self, writer)?;");          } +        self.writeln("Ok(())"); +        self.writeln("}"); +        self.writeln("}");      } -    fn _visit_format_filter(&mut self, args: &[Expr]) { -        self.write("format!("); -        self._visit_filter_args(args); -        self.write(")"); +    // Defines the `TraitFromPathName` trait. +    fn define_trait(&mut self, state: &'a State) { +        self.writeln(&format!("trait {} {{", state.trait_name)); +        self.write_block_defs(state); +        self.writeln(&format!( +            "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ +             -> ::askama::Result<()>;", +            state.trait_name)); +        self.writeln("}");      } -    // Force type coercion on first argument to `join` filter (see #39). -    fn _visit_join_filter(&mut self, args: &[Expr]) { -        self.write("::askama::filters::join((&"); -        for (i, arg) in args.iter().enumerate() { -            if i > 0 { -                self.write(", &"); -            } -            self.visit_expr(arg); -            if i == 0 { -                self.write(").into_iter()"); -            } -        } -        self.write(")?"); +    // Implement iron's Modifier<Response> if enabled +    fn impl_modifier_response(&mut self, state: &'a State) { +        self.write_header(state, "::askama::iron::Modifier<::askama::iron::Response>", &[]); +        self.writeln("fn modify(self, res: &mut ::askama::iron::Response) {"); +        self.writeln("res.body = Some(Box::new(self.render().unwrap().into_bytes()));"); +        self.writeln("}"); +        self.writeln("}");      } -    fn visit_filter(&mut self, name: &str, args: &[Expr]) -> DisplayWrap { -        if name == "format" { -            self._visit_format_filter(args); -            return DisplayWrap::Unwrapped; -        } else if name == "join" { -            self._visit_join_filter(args); -            return DisplayWrap::Unwrapped; -        } - -        if filters::BUILT_IN_FILTERS.contains(&name) { -            self.write(&format!("::askama::filters::{}(&", name)); -        } else { -            self.write(&format!("filters::{}(&", name)); -        } +    // Implement Rocket's `Responder`. +    fn impl_responder(&mut self, state: &'a State) { +        self.write_header(state, "::askama::rocket::Responder<'r>", &["'r"]); +        self.writeln("fn respond_to(self, _: &::askama::rocket::Request) \ +                      -> ::askama::rocket::Result<'r> {"); -        self._visit_filter_args(args); -        self.write(")?"); -        if name == "safe" || name == "escape" || name == "e" || name == "json" { -            DisplayWrap::Wrapped -        } else { -            DisplayWrap::Unwrapped -        } -    } +        let ext = match state.input.path.extension() { +            Some(s) => s.to_str().unwrap(), +            None => "txt", +        }; +        self.writeln(&format!("::askama::rocket::respond(&self, {:?})", ext)); -    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) -> DisplayWrap { -        self.visit_expr(left); -        self.write(&format!(" {} ", op)); -        self.visit_expr(right); -        DisplayWrap::Unwrapped +        self.dedent(); +        self.writeln("}"); +        self.writeln("}");      } -    fn visit_group(&mut self, inner: &Expr) -> DisplayWrap { -        self.write("("); -        self.visit_expr(inner); -        self.write(")"); -        DisplayWrap::Unwrapped -    } +    // Writes header for the `impl` for `TraitFromPathName` or `Template` +    // for the given context struct. +    fn write_header(&mut self, state: &'a State, target: &str, extra_anno: &[&str]) { +        let mut full_anno = Tokens::new(); +        let mut orig_anno = Tokens::new(); +        let need_anno = !state.input.ast.generics.lifetimes.is_empty() || +                        !state.input.ast.generics.ty_params.is_empty() || +                        !extra_anno.is_empty(); +        if need_anno { +            full_anno.append("<"); +            orig_anno.append("<"); +        } -    fn visit_method_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) -> DisplayWrap { -        self.visit_expr(obj); -        self.write(&format!(".{}(", method)); -        for (i, arg) in args.iter().enumerate() { -            if i > 0 { -                self.write(", "); +        let (mut full_sep, mut orig_sep) = (false, false); +        for lt in &state.input.ast.generics.lifetimes { +            if full_sep { +                full_anno.append(",");              } -            self.visit_expr(arg); +            if orig_sep { +                orig_anno.append(","); +            } +            lt.to_tokens(&mut full_anno); +            lt.to_tokens(&mut orig_anno); +            full_sep = true; +            orig_sep = true;          } -        self.write(")"); -        DisplayWrap::Unwrapped -    } -    fn visit_expr(&mut self, expr: &Expr) -> DisplayWrap { -        match *expr { -            Expr::NumLit(s) => self.visit_num_lit(s), -            Expr::StrLit(s) => self.visit_str_lit(s), -            Expr::Var(s) => self.visit_var(s), -            Expr::Attr(ref obj, name) => self.visit_attr(obj, name), -            Expr::Filter(name, ref args) => self.visit_filter(name, args), -            Expr::BinOp(op, ref left, ref right) => -                self.visit_binop(op, left, right), -            Expr::Group(ref inner) => self.visit_group(inner), -            Expr::MethodCall(ref obj, method, ref args) => -                self.visit_method_call(obj, method, args), +        for anno in extra_anno { +            if full_sep { +                full_anno.append(","); +            } +            full_anno.append(anno); +            full_sep = true;          } -    } -    fn visit_target_single<'t>(&mut self, name: &'t str) -> Vec<&'t str> { -        vec![name] -    } +        for param in &state.input.ast.generics.ty_params { +            if full_sep { +                full_anno.append(","); +            } +            if orig_sep { +                orig_anno.append(","); +            } +            let mut impl_param = param.clone(); +            impl_param.default = None; +            impl_param.to_tokens(&mut full_anno); +            param.ident.to_tokens(&mut orig_anno); +            full_sep = true; +            orig_sep = true; +        } -    fn visit_target<'t>(&mut self, target: &'t Target) -> Vec<&'t str> { -        match *target { -            Target::Name(s) => { self.visit_target_single(s) }, +        if need_anno { +            full_anno.append(">"); +            orig_anno.append(">");          } + +        let mut where_clause = Tokens::new(); +        state.input.ast.generics.where_clause.to_tokens(&mut where_clause); +        self.writeln(&format!("impl{} {} for {}{}{} {{", +                              full_anno.as_str(), target, state.input.ast.ident.as_ref(), +                              orig_anno.as_str(), where_clause.as_str()));      }      /* Helper methods for handling node types */ -    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) { -        assert!(self.next_ws.is_none()); -        if !lws.is_empty() { -            if self.skip_ws { -                self.skip_ws = false; -            } else if val.is_empty() { -                assert!(rws.is_empty()); -                self.next_ws = Some(lws); -            } else { -                self.writeln(&format!("writer.write_str({:#?})?;", -                                      lws)); +    fn handle(&mut self, state: &'a State, nodes: &'a [Node], level: AstLevel) { +        for n in nodes { +            match *n { +                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } +                Node::Comment() => {}, +                Node::Expr(ref ws, ref val) => { self.write_expr(state, ws, val); }, +                Node::LetDecl(ref ws, ref var) => { self.write_let_decl(ws, var); }, +                Node::Let(ref ws, ref var, ref val) => { self.write_let(ws, var, val); }, +                Node::Cond(ref conds, ref ws) => { +                    self.write_cond(state, conds, ws); +                }, +                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => { +                    self.write_loop(state, ws1, var, iter, body, ws2); +                }, +                Node::BlockDef(ref ws1, name, _, ref ws2) => { +                    if let AstLevel::Nested = level { +                        panic!("blocks ('{}') are only allowed at the top level", name); +                    } +                    self.write_block(ws1, name, ws2); +                }, +                Node::Include(ref ws, path) => { +                    self.handle_include(state, ws, path); +                }, +                Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), +                Node::Macro(_, _) | +                Node::Extends(_) => { +                    if let AstLevel::Nested = level { +                        panic!("macro or extend blocks only allowed at the top level"); +                    } +                },              }          } -        if !val.is_empty() { -            self.writeln(&format!("writer.write_str({:#?})?;", val)); -        } -        if !rws.is_empty() { -            self.next_ws = Some(rws); -        } -    } - -    fn write_expr(&mut self, state: &'a State, ws: &WS, s: &Expr) { -        self.handle_ws(ws); -        self.write("let askama_expr = &"); -        let wrapped = self.visit_expr(s); -        self.writeln(";"); - -        use self::DisplayWrap::*; -        use super::input::EscapeMode::*; -        self.write("writer.write_fmt(format_args!(\"{}\", "); -        self.write(match (wrapped, &state.input.meta.escaping) { -            (Wrapped, &Html) | -            (Wrapped, &None) | -            (Unwrapped, &None) => "askama_expr", -            (Unwrapped, &Html) => "&::askama::MarkupDisplay::from(askama_expr)", -        }); -        self.writeln("))?;");      } -    fn write_call(&mut self, state: &'a State, ws: &WS, name: &str, args: &[Expr]) { -        let def = state.macros.get(name).expect(&format!("macro '{}' not found", name)); -        self.handle_ws(ws); -        self.locals.push(); -        self.writeln("{"); -        self.prepare_ws(&def.ws1); -        for (i, arg) in def.args.iter().enumerate() { -            self.write(&format!("let {} = &", arg)); -            self.locals.insert(arg); -            self.visit_expr(args.get(i) -                .expect(&format!("macro '{}' takes more than {} arguments", name, i))); -            self.writeln(";"); -        } -        self.handle(state, &def.nodes, AstLevel::Nested); -        self.flush_ws(&def.ws2); -        self.writeln("}"); -        self.locals.pop(); -    } +    fn write_block_defs(&mut self, state: &'a State) { +        for b in &state.blocks { +            if let Node::BlockDef(ref ws1, name, ref nodes, ref ws2) = **b { +                self.writeln("#[allow(unused_variables)]"); +                self.writeln(&format!( +                    "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \ +                     -> ::askama::Result<()> {{", +                    name)); +                self.prepare_ws(ws1); -    fn write_let_decl(&mut self, ws: &WS, var: &'a Target) { -        self.handle_ws(ws); -        self.write("let "); -        match *var { -            Target::Name(name) => { -                self.locals.insert(name); -                self.write(name); -            }, -        } -        self.writeln(";"); -    } +                self.locals.push(); +                self.handle(state, nodes, AstLevel::Nested); +                self.locals.pop(); -    fn write_let(&mut self, ws: &WS, var: &'a Target, val: &Expr) { -        self.handle_ws(ws); -        match *var { -            Target::Name(name) => { -                if !self.locals.contains(name) { -                    self.write("let "); -                    self.locals.insert(name); -                } -                self.write(name); -            }, +                self.flush_ws(ws2); +                self.writeln("Ok(())"); +                self.writeln("}"); +            } else { +                panic!("only block definitions allowed here"); +            }          } -        self.write(" = "); -        self.visit_expr(val); -        self.writeln(";");      }      fn write_cond(&mut self, state: &'a State, conds: &'a [Cond], ws: &WS) { @@ -464,33 +429,23 @@ impl<'a> Generator<'a> {          self.locals.pop();      } -    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { -        self.flush_ws(ws1); -        self.writeln(&format!("timpl.render_block_{}_into(writer)?;", name)); -        self.prepare_ws(ws2); -    } - -    fn write_block_defs(&mut self, state: &'a State) { -        for b in &state.blocks { -            if let Node::BlockDef(ref ws1, name, ref nodes, ref ws2) = **b { -                self.writeln("#[allow(unused_variables)]"); -                self.writeln(&format!( -                    "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \ -                     -> ::askama::Result<()> {{", -                    name)); -                self.prepare_ws(ws1); - -                self.locals.push(); -                self.handle(state, nodes, AstLevel::Nested); -                self.locals.pop(); - -                self.flush_ws(ws2); -                self.writeln("Ok(())"); -                self.writeln("}"); -            } else { -                panic!("only block definitions allowed here"); -            } +    fn write_call(&mut self, state: &'a State, ws: &WS, name: &str, args: &[Expr]) { +        let def = state.macros.get(name).expect(&format!("macro '{}' not found", name)); +        self.handle_ws(ws); +        self.locals.push(); +        self.writeln("{"); +        self.prepare_ws(&def.ws1); +        for (i, arg) in def.args.iter().enumerate() { +            self.write(&format!("let {} = &", arg)); +            self.locals.insert(arg); +            self.visit_expr(args.get(i) +                .expect(&format!("macro '{}' takes more than {} arguments", name, i))); +            self.writeln(";");          } +        self.handle(state, &def.nodes, AstLevel::Nested); +        self.flush_ws(&def.ws2); +        self.writeln("}"); +        self.locals.pop();      }      fn handle_include(&mut self, state: &'a State, ws: &WS, path: &str) { @@ -507,235 +462,280 @@ impl<'a> Generator<'a> {          self.flush_ws(ws);      } -    fn handle(&mut self, state: &'a State, nodes: &'a [Node], level: AstLevel) { -        for n in nodes { -            match *n { -                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } -                Node::Comment() => {}, -                Node::Expr(ref ws, ref val) => { self.write_expr(state, ws, val); }, -                Node::LetDecl(ref ws, ref var) => { self.write_let_decl(ws, var); }, -                Node::Let(ref ws, ref var, ref val) => { self.write_let(ws, var, val); }, -                Node::Cond(ref conds, ref ws) => { -                    self.write_cond(state, conds, ws); -                }, -                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => { -                    self.write_loop(state, ws1, var, iter, body, ws2); -                }, -                Node::BlockDef(ref ws1, name, _, ref ws2) => { -                    if let AstLevel::Nested = level { -                        panic!("blocks ('{}') are only allowed at the top level", name); -                    } -                    self.write_block(ws1, name, ws2); -                }, -                Node::Include(ref ws, path) => { -                    self.handle_include(state, ws, path); -                }, -                Node::Call(ref ws, name, ref args) => self.write_call(state, ws, name, args), -                Node::Macro(_, _) | -                Node::Extends(_) => { -                    if let AstLevel::Nested = level { -                        panic!("macro or extend blocks only allowed at the top level"); -                    } -                }, -            } +    fn write_let_decl(&mut self, ws: &WS, var: &'a Target) { +        self.handle_ws(ws); +        self.write("let "); +        match *var { +            Target::Name(name) => { +                self.locals.insert(name); +                self.write(name); +            },          } +        self.writeln(";");      } -    // Writes header for the `impl` for `TraitFromPathName` or `Template` -    // for the given context struct. -    fn write_header(&mut self, state: &'a State, target: &str, extra_anno: &[&str]) { -        let mut full_anno = Tokens::new(); -        let mut orig_anno = Tokens::new(); -        let need_anno = !state.input.ast.generics.lifetimes.is_empty() || -                        !state.input.ast.generics.ty_params.is_empty() || -                        !extra_anno.is_empty(); -        if need_anno { -            full_anno.append("<"); -            orig_anno.append("<"); +    fn write_let(&mut self, ws: &WS, var: &'a Target, val: &Expr) { +        self.handle_ws(ws); +        match *var { +            Target::Name(name) => { +                if !self.locals.contains(name) { +                    self.write("let "); +                    self.locals.insert(name); +                } +                self.write(name); +            },          } +        self.write(" = "); +        self.visit_expr(val); +        self.writeln(";"); +    } -        let (mut full_sep, mut orig_sep) = (false, false); -        for lt in &state.input.ast.generics.lifetimes { -            if full_sep { -                full_anno.append(","); -            } -            if orig_sep { -                orig_anno.append(","); +    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { +        self.flush_ws(ws1); +        self.writeln(&format!("timpl.render_block_{}_into(writer)?;", name)); +        self.prepare_ws(ws2); +    } + +    fn write_expr(&mut self, state: &'a State, ws: &WS, s: &Expr) { +        self.handle_ws(ws); +        self.write("let askama_expr = &"); +        let wrapped = self.visit_expr(s); +        self.writeln(";"); + +        use self::DisplayWrap::*; +        use super::input::EscapeMode::*; +        self.write("writer.write_fmt(format_args!(\"{}\", "); +        self.write(match (wrapped, &state.input.meta.escaping) { +            (Wrapped, &Html) | +            (Wrapped, &None) | +            (Unwrapped, &None) => "askama_expr", +            (Unwrapped, &Html) => "&::askama::MarkupDisplay::from(askama_expr)", +        }); +        self.writeln("))?;"); +    } + +    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) { +        assert!(self.next_ws.is_none()); +        if !lws.is_empty() { +            if self.skip_ws { +                self.skip_ws = false; +            } else if val.is_empty() { +                assert!(rws.is_empty()); +                self.next_ws = Some(lws); +            } else { +                self.writeln(&format!("writer.write_str({:#?})?;", +                                      lws));              } -            lt.to_tokens(&mut full_anno); -            lt.to_tokens(&mut orig_anno); -            full_sep = true; -            orig_sep = true;          } +        if !val.is_empty() { +            self.writeln(&format!("writer.write_str({:#?})?;", val)); +        } +        if !rws.is_empty() { +            self.next_ws = Some(rws); +        } +    } -        for anno in extra_anno { -            if full_sep { -                full_anno.append(","); -            } -            full_anno.append(anno); -            full_sep = true; +    /* Visitor methods for expression types */ + +    fn visit_expr(&mut self, expr: &Expr) -> DisplayWrap { +        match *expr { +            Expr::NumLit(s) => self.visit_num_lit(s), +            Expr::StrLit(s) => self.visit_str_lit(s), +            Expr::Var(s) => self.visit_var(s), +            Expr::Attr(ref obj, name) => self.visit_attr(obj, name), +            Expr::Filter(name, ref args) => self.visit_filter(name, args), +            Expr::BinOp(op, ref left, ref right) => +                self.visit_binop(op, left, right), +            Expr::Group(ref inner) => self.visit_group(inner), +            Expr::MethodCall(ref obj, method, ref args) => +                self.visit_method_call(obj, method, args),          } +    } -        for param in &state.input.ast.generics.ty_params { -            if full_sep { -                full_anno.append(","); -            } -            if orig_sep { -                orig_anno.append(","); -            } -            let mut impl_param = param.clone(); -            impl_param.default = None; -            impl_param.to_tokens(&mut full_anno); -            param.ident.to_tokens(&mut orig_anno); -            full_sep = true; -            orig_sep = true; +    fn visit_filter(&mut self, name: &str, args: &[Expr]) -> DisplayWrap { +        if name == "format" { +            self._visit_format_filter(args); +            return DisplayWrap::Unwrapped; +        } else if name == "join" { +            self._visit_join_filter(args); +            return DisplayWrap::Unwrapped;          } -        if need_anno { -            full_anno.append(">"); -            orig_anno.append(">"); +        if filters::BUILT_IN_FILTERS.contains(&name) { +            self.write(&format!("::askama::filters::{}(&", name)); +        } else { +            self.write(&format!("filters::{}(&", name));          } -        let mut where_clause = Tokens::new(); -        state.input.ast.generics.where_clause.to_tokens(&mut where_clause); -        self.writeln(&format!("impl{} {} for {}{}{} {{", -                              full_anno.as_str(), target, state.input.ast.ident.as_ref(), -                              orig_anno.as_str(), where_clause.as_str())); +        self._visit_filter_args(args); +        self.write(")?"); +        if name == "safe" || name == "escape" || name == "e" || name == "json" { +            DisplayWrap::Wrapped +        } else { +            DisplayWrap::Unwrapped +        }      } -    // Implement `Template` for the given context struct. -    fn impl_template(&mut self, state: &'a State) { -        self.write_header(state, "::askama::Template", &[]); -        self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) -> \ -                      ::askama::Result<()> {"); -        self.handle(state, state.nodes, AstLevel::Top); -        self.flush_ws(&WS(false, false)); -        self.writeln("Ok(())"); -        self.writeln("}"); -        self.writeln("}"); +    fn _visit_format_filter(&mut self, args: &[Expr]) { +        self.write("format!("); +        self._visit_filter_args(args); +        self.write(")");      } -    // Implement `Display` for the given context struct. -    fn impl_display(&mut self, state: &'a State) { -        self.write_header(state, "::std::fmt::Display", &[]); -        self.writeln("fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {"); -        self.writeln("self.render_into(f).map_err(|_| ::std::fmt::Error {})"); -        self.writeln("}"); -        self.writeln("}"); +    // Force type coercion on first argument to `join` filter (see #39). +    fn _visit_join_filter(&mut self, args: &[Expr]) { +        self.write("::askama::filters::join((&"); +        for (i, arg) in args.iter().enumerate() { +            if i > 0 { +                self.write(", &"); +            } +            self.visit_expr(arg); +            if i == 0 { +                self.write(").into_iter()"); +            } +        } +        self.write(")?");      } -    // Implement `Deref<Parent>` for an inheriting context struct. -    fn deref_to_parent(&mut self, state: &'a State, parent_type: &syn::Ty) { -        self.write_header(state, "::std::ops::Deref", &[]); -        let mut tokens = Tokens::new(); -        parent_type.to_tokens(&mut tokens); -        self.writeln(&format!("type Target = {};", tokens.as_str())); -        self.writeln("fn deref(&self) -> &Self::Target {"); -        self.writeln("&self._parent"); -        self.writeln("}"); -        self.writeln("}"); +    fn _visit_filter_args(&mut self, args: &[Expr]) { +        for (i, arg) in args.iter().enumerate() { +            if i > 0 { +                self.write(", &"); +            } +            self.visit_expr(arg); +        }      } -    // Implement `TraitFromPathName` for the given context struct. -    fn impl_trait(&mut self, state: &'a State, nodes: Option<&'a [Node]>) { -        self.write_header(state, &state.trait_name, &[]); -        self.write_block_defs(state); - -        self.writeln("#[allow(unused_variables)]"); -        self.writeln(&format!( -            "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ -             -> ::askama::Result<()> {{", -            state.trait_name)); +    fn visit_attr(&mut self, obj: &Expr, attr: &str) -> DisplayWrap { +        if let Expr::Var(name) = *obj { +            if name == "loop" { +                self.write("_loop_index"); +                if attr == "index" { +                    self.write(" + 1"); +                    return DisplayWrap::Unwrapped; +                } else if attr == "index0" { +                    return DisplayWrap::Unwrapped; +                } else { +                    panic!("unknown loop variable"); +                } +            } +        } +        self.visit_expr(obj); +        self.write(&format!(".{}", attr)); +        DisplayWrap::Unwrapped +    } -        if let Some(nodes) = nodes { -            self.handle(state, nodes, AstLevel::Top); -            self.flush_ws(&WS(false, false)); -        } else { -            self.writeln("self._parent.render_trait_into(self, writer)?;"); +    fn visit_method_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) -> DisplayWrap { +        self.visit_expr(obj); +        self.write(&format!(".{}(", method)); +        for (i, arg) in args.iter().enumerate() { +            if i > 0 { +                self.write(", "); +            } +            self.visit_expr(arg);          } +        self.write(")"); +        DisplayWrap::Unwrapped +    } -        self.writeln("Ok(())"); -        self.writeln("}"); -        self.flush_ws(&WS(false, false)); -        self.writeln("}"); +    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) -> DisplayWrap { +        self.visit_expr(left); +        self.write(&format!(" {} ", op)); +        self.visit_expr(right); +        DisplayWrap::Unwrapped      } -    // Implement `Template` for templates that implement a template trait. -    fn impl_template_for_trait(&mut self, state: &'a State) { -        self.write_header(state, "::askama::Template", &[]); -        self.writeln("fn render_into(&self, writer: &mut ::std::fmt::Write) \ -                      -> ::askama::Result<()> {"); -        if state.derived { -            self.writeln("self._parent.render_trait_into(self, writer)?;"); +    fn visit_group(&mut self, inner: &Expr) -> DisplayWrap { +        self.write("("); +        self.visit_expr(inner); +        self.write(")"); +        DisplayWrap::Unwrapped +    } + +    fn visit_var(&mut self, s: &str) -> DisplayWrap { +        if self.locals.contains(s) { +            self.write(s);          } else { -            self.writeln("self.render_trait_into(self, writer)?;"); +            self.write(&format!("self.{}", s));          } -        self.writeln("Ok(())"); -        self.writeln("}"); -        self.writeln("}"); +        DisplayWrap::Unwrapped      } -    // Defines the `TraitFromPathName` trait. -    fn define_trait(&mut self, state: &'a State) { -        self.writeln(&format!("trait {} {{", state.trait_name)); -        self.write_block_defs(state); -        self.writeln(&format!( -            "fn render_trait_into(&self, timpl: &{}, writer: &mut ::std::fmt::Write) \ -             -> ::askama::Result<()>;", -            state.trait_name)); -        self.writeln("}"); +    fn visit_str_lit(&mut self, s: &str) -> DisplayWrap { +        self.write(&format!("\"{}\"", s)); +        DisplayWrap::Unwrapped      } -    // Implement iron's Modifier<Response> if enabled -    fn impl_modifier_response(&mut self, state: &'a State) { -        self.write_header(state, "::askama::iron::Modifier<::askama::iron::Response>", &[]); -        self.writeln("fn modify(self, res: &mut ::askama::iron::Response) {"); -        self.writeln("res.body = Some(Box::new(self.render().unwrap().into_bytes()));"); -        self.writeln("}"); -        self.writeln("}"); +    fn visit_num_lit(&mut self, s: &str) -> DisplayWrap { +        self.write(s); +        DisplayWrap::Unwrapped      } -    // Implement Rocket's `Responder`. -    fn impl_responder(&mut self, state: &'a State) { -        self.write_header(state, "::askama::rocket::Responder<'r>", &["'r"]); -        self.writeln("fn respond_to(self, _: &::askama::rocket::Request) \ -                      -> ::askama::rocket::Result<'r> {"); +    fn visit_target_single<'t>(&mut self, name: &'t str) -> Vec<&'t str> { +        vec![name] +    } -        let ext = match state.input.path.extension() { -            Some(s) => s.to_str().unwrap(), -            None => "txt", -        }; -        self.writeln(&format!("::askama::rocket::respond(&self, {:?})", ext)); +    fn visit_target<'t>(&mut self, target: &'t Target) -> Vec<&'t str> { +        match *target { +            Target::Name(s) => { self.visit_target_single(s) }, +        } +    } -        self.dedent(); -        self.writeln("}"); -        self.writeln("}"); +    /* Helper methods for dealing with whitespace nodes */ + +    fn handle_ws(&mut self, ws: &WS) { +        self.flush_ws(ws); +        self.prepare_ws(ws);      } -    fn result(mut self, state: &'a State) -> String { -        if !state.blocks.is_empty() { -            if !state.derived { -                self.define_trait(state); -            } else { -                let parent_type = get_parent_type(state.input.ast) -                    .expect("expected field '_parent' in extending template struct"); -                self.deref_to_parent(state, parent_type); +    fn flush_ws(&mut self, ws: &WS) { +        if self.next_ws.is_some() && !ws.0 { +            let val = self.next_ws.unwrap(); +            if !val.is_empty() { +                self.writeln(&format!("writer.write_str({:#?})?;", +                                      val));              } +        } +        self.next_ws = None; +    } -            let trait_nodes = if !state.derived { Some(&state.nodes[..]) } else { None }; -            self.impl_trait(state, trait_nodes); -            self.impl_template_for_trait(state); -        } else { -            self.impl_template(state); +    fn prepare_ws(&mut self, ws: &WS) { +        self.skip_ws = ws.1; +    } + +    /* Helper methods for writing to internal buffer */ + +    fn writeln(&mut self, s: &str) { +        if s.is_empty() { +            return;          } -        self.impl_display(state); -        if cfg!(feature = "iron") { -            self.impl_modifier_response(state); +        if s == "}" { +            self.dedent();          } -        if cfg!(feature = "rocket") { -            self.impl_responder(state); +        self.write(s); +        if s.ends_with('{') { +            self.indent();          } -        self.buf +        self.buf.push('\n'); +        self.start = true; +    } + +    fn write(&mut self, s: &str) { +        if self.start { +            for _ in 0..(self.indent * 4) { +                self.buf.push(' '); +            } +            self.start = false; +        } +        self.buf.push_str(s); +    } + +    fn indent(&mut self) { +        self.indent += 1; +    } + +    fn dedent(&mut self) { +        self.indent -= 1;      }  } | 
