Shorthand syntax for rendering a TemplateOnce within another template

This commit is contained in:
Daniel Arbuckle 2023-01-19 03:07:43 -08:00
parent f4cf0a9ab0
commit c282daf907
2 changed files with 92 additions and 1 deletions

View File

@ -56,6 +56,7 @@ impl Default for Parser {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenKind {
NestedTemplateOnce,
BufferedCode { escape: bool },
Code,
Comment,
@ -161,6 +162,10 @@ impl<'a> ParseStream<'a> {
token_kind = TokenKind::BufferedCode { escape: false };
start += 1;
}
Some(b'^') => {
token_kind = TokenKind::NestedTemplateOnce;
start += 1;
}
_ => {}
}
@ -402,6 +407,33 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn nested_render_once() {
let src = r#"outer <%^ inner|upper %> outer"#;
let parser = Parser::default();
let tokens = parser.parse(src).into_vec().unwrap();
assert_eq!(
&tokens,
&[
Token {
content: "outer ",
offset: 0,
kind: TokenKind::Text,
},
Token {
content: "inner|upper",
offset: 10,
kind: TokenKind::NestedTemplateOnce,
},
Token {
content: " outer",
offset: 24,
kind: TokenKind::Text,
},
]
);
}
#[test]
fn non_ascii_delimiter() {
let src = r##"foo <🍣# This is a comment 🍣> bar <🍣= r"🍣>" 🍣> baz <🍣🍣"##;

View File

@ -135,6 +135,15 @@ impl SourceBuilder {
&mut self,
token: &Token<'a>,
escape: bool,
) -> Result<(), Error> {
self.write_buffered_code_with_suffix(token, escape, "")
}
fn write_buffered_code_with_suffix<'a>(
&mut self,
token: &Token<'a>,
escape: bool,
suffix: &str,
) -> Result<(), Error> {
// parse and split off filter
let code_block = syn::parse_str::<CodeBlock>(token.as_str()).map_err(|e| {
@ -154,7 +163,7 @@ impl SourceBuilder {
self.source.push_str("!(__sf_buf, ");
if let Some(filter) = code_block.filter {
let expr_str = code_block.expr.into_token_stream().to_string();
let expr_str = format!("{}{}", code_block.expr.into_token_stream(), suffix);
let (name, extra_args) = match filter {
Filter::Ident(i) => (i.to_string(), None),
Filter::Call(c) => (
@ -188,6 +197,7 @@ impl SourceBuilder {
self.source.push(')');
} else {
self.write_token(token);
self.source.push_str(suffix);
}
self.source.push_str(");\n");
@ -205,6 +215,11 @@ impl SourceBuilder {
TokenKind::BufferedCode { escape } => {
self.write_buffered_code(&token, escape)?
}
TokenKind::NestedTemplateOnce => self.write_buffered_code_with_suffix(
&token,
false,
".render_once()?",
)?,
TokenKind::Text => {
// concatenate repeated text token
let offset = token.offset();
@ -367,4 +382,48 @@ mod tests {
ps.feed_tokens(token_iter.clone()).unwrap();
Translator::new().translate(token_iter).unwrap();
}
#[test]
fn translate_nested_render_once() {
let src = r#"outer <%^ inner %> outer"#;
let lexer = Parser::new();
let token_iter = lexer.parse(src);
let mut ps = SourceBuilder {
escape: true,
source: String::with_capacity(token_iter.original_source.len()),
source_map: SourceMap::default(),
};
ps.feed_tokens(token_iter.clone()).unwrap();
assert_eq!(
&Translator::new()
.translate(token_iter)
.unwrap()
.ast
.into_token_stream()
.to_string(),
r#"{ __sf_rt :: render_text ! (__sf_buf , "outer ") ; __sf_rt :: render ! (__sf_buf , inner . render_once () ?) ; __sf_rt :: render_text ! (__sf_buf , " outer") ; }"#
);
}
#[test]
fn translate_nested_render_once_with_filter() {
let src = r#"outer <%^ inner|upper %> outer"#;
let lexer = Parser::new();
let token_iter = lexer.parse(src);
let mut ps = SourceBuilder {
escape: true,
source: String::with_capacity(token_iter.original_source.len()),
source_map: SourceMap::default(),
};
ps.feed_tokens(token_iter.clone()).unwrap();
assert_eq!(
&Translator::new()
.translate(token_iter)
.unwrap()
.ast
.into_token_stream()
.to_string(),
r#"{ __sf_rt :: render_text ! (__sf_buf , "outer ") ; __sf_rt :: render ! (__sf_buf , sailfish :: runtime :: filter :: upper (& (inner . render_once () ?))) ; __sf_rt :: render_text ! (__sf_buf , " outer") ; }"#
);
}
}