mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-29 22:11:17 +00:00
Fix block partial rendering
This commit is contained in:
parent
b11d99c58e
commit
e2eb76b860
@ -47,6 +47,7 @@ impl<'a> Generator<'a> {
|
||||
contexts: &'n HashMap<&'n Rc<Path>, Context<'n>>,
|
||||
heritage: Option<&'n Heritage<'_>>,
|
||||
locals: MapChain<'n, Cow<'n, str>, LocalMeta>,
|
||||
buf_writable_discard: bool,
|
||||
) -> Generator<'n> {
|
||||
Generator {
|
||||
input,
|
||||
@ -57,7 +58,7 @@ impl<'a> Generator<'a> {
|
||||
skip_ws: WhitespaceHandling::Preserve,
|
||||
super_block: None,
|
||||
buf_writable: WritableBuffer {
|
||||
discard: input.block.is_some(),
|
||||
discard: buf_writable_discard,
|
||||
..Default::default()
|
||||
},
|
||||
named: 0,
|
||||
@ -90,6 +91,7 @@ impl<'a> Generator<'a> {
|
||||
buf.write(CRATE);
|
||||
buf.writeln("::Result<()> {")?;
|
||||
|
||||
buf.discard = self.buf_writable.discard;
|
||||
// Make sure the compiler understands that the generated code depends on the template files.
|
||||
for path in self.contexts.keys() {
|
||||
// Skip the fake path of templates defined in rust source.
|
||||
@ -113,6 +115,7 @@ impl<'a> Generator<'a> {
|
||||
} else {
|
||||
self.handle(ctx, ctx.nodes, buf, AstLevel::Top)
|
||||
}?;
|
||||
buf.discard = false;
|
||||
|
||||
self.flush_ws(Ws(None, None));
|
||||
buf.write(CRATE);
|
||||
@ -829,7 +832,13 @@ impl<'a> Generator<'a> {
|
||||
None => child_ctx,
|
||||
};
|
||||
let locals = MapChain::with_parent(&self.locals);
|
||||
let mut child = Self::new(self.input, self.contexts, heritage.as_ref(), locals);
|
||||
let mut child = Self::new(
|
||||
self.input,
|
||||
self.contexts,
|
||||
heritage.as_ref(),
|
||||
locals,
|
||||
self.buf_writable.discard,
|
||||
);
|
||||
let mut size_hint = child.handle(handle_ctx, handle_ctx.nodes, buf, AstLevel::Top)?;
|
||||
size_hint += child.write_buf_writable(handle_ctx, buf)?;
|
||||
self.prepare_ws(i.ws);
|
||||
@ -992,6 +1001,7 @@ impl<'a> Generator<'a> {
|
||||
Some(heritage),
|
||||
// Variables are NOT inherited from the parent scope.
|
||||
MapChain::default(),
|
||||
self.buf_writable.discard,
|
||||
);
|
||||
child.buf_writable = mem::take(&mut self.buf_writable);
|
||||
|
||||
@ -1013,6 +1023,13 @@ impl<'a> Generator<'a> {
|
||||
// succeeding whitespace according to the outer WS spec
|
||||
self.prepare_ws(outer);
|
||||
|
||||
// If we are rendering a specific block and the discard changed, it means that we're done
|
||||
// with the block we want to render and that from this point, everything will be discarded.
|
||||
//
|
||||
// To get this block content rendered as well, we need to write to the buffer before then.
|
||||
if buf.discard != prev_buf_discard {
|
||||
self.write_buf_writable(ctx, buf)?;
|
||||
}
|
||||
// Restore the original buffer discarding state
|
||||
if block_fragment_write {
|
||||
self.buf_writable.discard = true;
|
||||
@ -1910,6 +1927,7 @@ impl<'a> Generator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Buffer {
|
||||
// The buffer to generate the code into
|
||||
buf: String,
|
||||
@ -2269,6 +2287,7 @@ struct WriteParts {
|
||||
/// ```ignore
|
||||
/// let var = format!(format, expr);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
struct WritePartsBuffers {
|
||||
format: Buffer,
|
||||
expr: Option<Buffer>,
|
||||
|
@ -44,7 +44,14 @@ fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
|
||||
let mut contexts = HashMap::new();
|
||||
let parsed = parser::Parsed::default();
|
||||
contexts.insert(&input.path, Context::empty(&parsed));
|
||||
Generator::new(&input, &contexts, None, MapChain::default()).build(&contexts[&input.path])
|
||||
Generator::new(
|
||||
&input,
|
||||
&contexts,
|
||||
None,
|
||||
MapChain::default(),
|
||||
input.block.is_some(),
|
||||
)
|
||||
.build(&contexts[&input.path])
|
||||
}
|
||||
|
||||
/// Takes a `syn::DeriveInput` and generates source code for it
|
||||
@ -91,8 +98,14 @@ pub(crate) fn build_template(ast: &syn::DeriveInput) -> Result<String, CompileEr
|
||||
eprintln!("{:?}", templates[&input.path].nodes());
|
||||
}
|
||||
|
||||
let code = Generator::new(&input, &contexts, heritage.as_ref(), MapChain::default())
|
||||
.build(&contexts[&input.path])?;
|
||||
let code = Generator::new(
|
||||
&input,
|
||||
&contexts,
|
||||
heritage.as_ref(),
|
||||
MapChain::default(),
|
||||
input.block.is_some(),
|
||||
)
|
||||
.build(&contexts[&input.path])?;
|
||||
if input.print == Print::Code || input.print == Print::All {
|
||||
eprintln!("{code}");
|
||||
}
|
||||
|
@ -103,3 +103,45 @@ fn test_specific_block() {
|
||||
let t = RenderInPlace { s1 };
|
||||
assert_eq!(t.render().unwrap(), "\nSection: [abc]\n");
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"{% block empty %}
|
||||
{% endblock %}
|
||||
|
||||
{% if let Some(var) = var %}
|
||||
{{ var }}
|
||||
{% endif %}"#,
|
||||
block = "empty",
|
||||
ext = "txt"
|
||||
)]
|
||||
struct Empty {}
|
||||
|
||||
#[test]
|
||||
fn test_render_only_block() {
|
||||
assert_eq!(Empty {}.render().unwrap(), "\n");
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(
|
||||
source = r#"{% extends "fragment-base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% include "included.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block other_body %}
|
||||
<p>Don't render me.</p>
|
||||
{% endblock %}"#,
|
||||
block = "body",
|
||||
ext = "html"
|
||||
)]
|
||||
struct FragmentInclude<'a> {
|
||||
s: &'a str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fragment_include() {
|
||||
let fragment_include = FragmentInclude { s: "world" };
|
||||
assert_eq!(fragment_include.render().unwrap(), "\nINCLUDED: world\n");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user