Skip to content

Commit

Permalink
Fix block partial rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed Jun 17, 2024
1 parent b11d99c commit e2eb76b
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
23 changes: 21 additions & 2 deletions rinja_derive/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down Expand Up @@ -1910,6 +1927,7 @@ impl<'a> Generator<'a> {
}
}

#[derive(Debug)]
struct Buffer {
// The buffer to generate the code into
buf: String,
Expand Down Expand Up @@ -2269,6 +2287,7 @@ struct WriteParts {
/// ```ignore
/// let var = format!(format, expr);
/// ```
#[derive(Debug)]
struct WritePartsBuffers {
format: Buffer,
expr: Option<Buffer>,
Expand Down
19 changes: 16 additions & 3 deletions rinja_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}");
}
Expand Down
42 changes: 42 additions & 0 deletions testing/tests/block_fragments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

0 comments on commit e2eb76b

Please sign in to comment.