Skip to content

Commit 28eeba0

Browse files
Merge pull request #3 from GuillaumeGomez/fix-block-partial-rendering
Fix block partial rendering
2 parents 64dcc7c + e2eb76b commit 28eeba0

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

rinja_derive/src/generator.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl<'a> Generator<'a> {
4747
contexts: &'n HashMap<&'n Rc<Path>, Context<'n>>,
4848
heritage: Option<&'n Heritage<'_>>,
4949
locals: MapChain<'n, Cow<'n, str>, LocalMeta>,
50+
buf_writable_discard: bool,
5051
) -> Generator<'n> {
5152
Generator {
5253
input,
@@ -57,7 +58,7 @@ impl<'a> Generator<'a> {
5758
skip_ws: WhitespaceHandling::Preserve,
5859
super_block: None,
5960
buf_writable: WritableBuffer {
60-
discard: input.block.is_some(),
61+
discard: buf_writable_discard,
6162
..Default::default()
6263
},
6364
named: 0,
@@ -90,6 +91,7 @@ impl<'a> Generator<'a> {
9091
buf.write(CRATE);
9192
buf.writeln("::Result<()> {")?;
9293

94+
buf.discard = self.buf_writable.discard;
9395
// Make sure the compiler understands that the generated code depends on the template files.
9496
for path in self.contexts.keys() {
9597
// Skip the fake path of templates defined in rust source.
@@ -113,6 +115,7 @@ impl<'a> Generator<'a> {
113115
} else {
114116
self.handle(ctx, ctx.nodes, buf, AstLevel::Top)
115117
}?;
118+
buf.discard = false;
116119

117120
self.flush_ws(Ws(None, None));
118121
buf.write(CRATE);
@@ -829,7 +832,13 @@ impl<'a> Generator<'a> {
829832
None => child_ctx,
830833
};
831834
let locals = MapChain::with_parent(&self.locals);
832-
let mut child = Self::new(self.input, self.contexts, heritage.as_ref(), locals);
835+
let mut child = Self::new(
836+
self.input,
837+
self.contexts,
838+
heritage.as_ref(),
839+
locals,
840+
self.buf_writable.discard,
841+
);
833842
let mut size_hint = child.handle(handle_ctx, handle_ctx.nodes, buf, AstLevel::Top)?;
834843
size_hint += child.write_buf_writable(handle_ctx, buf)?;
835844
self.prepare_ws(i.ws);
@@ -992,6 +1001,7 @@ impl<'a> Generator<'a> {
9921001
Some(heritage),
9931002
// Variables are NOT inherited from the parent scope.
9941003
MapChain::default(),
1004+
self.buf_writable.discard,
9951005
);
9961006
child.buf_writable = mem::take(&mut self.buf_writable);
9971007

@@ -1013,6 +1023,13 @@ impl<'a> Generator<'a> {
10131023
// succeeding whitespace according to the outer WS spec
10141024
self.prepare_ws(outer);
10151025

1026+
// If we are rendering a specific block and the discard changed, it means that we're done
1027+
// with the block we want to render and that from this point, everything will be discarded.
1028+
//
1029+
// To get this block content rendered as well, we need to write to the buffer before then.
1030+
if buf.discard != prev_buf_discard {
1031+
self.write_buf_writable(ctx, buf)?;
1032+
}
10161033
// Restore the original buffer discarding state
10171034
if block_fragment_write {
10181035
self.buf_writable.discard = true;
@@ -1910,6 +1927,7 @@ impl<'a> Generator<'a> {
19101927
}
19111928
}
19121929

1930+
#[derive(Debug)]
19131931
struct Buffer {
19141932
// The buffer to generate the code into
19151933
buf: String,
@@ -2269,6 +2287,7 @@ struct WriteParts {
22692287
/// ```ignore
22702288
/// let var = format!(format, expr);
22712289
/// ```
2290+
#[derive(Debug)]
22722291
struct WritePartsBuffers {
22732292
format: Buffer,
22742293
expr: Option<Buffer>,

rinja_derive/src/lib.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
4444
let mut contexts = HashMap::new();
4545
let parsed = parser::Parsed::default();
4646
contexts.insert(&input.path, Context::empty(&parsed));
47-
Generator::new(&input, &contexts, None, MapChain::default()).build(&contexts[&input.path])
47+
Generator::new(
48+
&input,
49+
&contexts,
50+
None,
51+
MapChain::default(),
52+
input.block.is_some(),
53+
)
54+
.build(&contexts[&input.path])
4855
}
4956

5057
/// 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
9198
eprintln!("{:?}", templates[&input.path].nodes());
9299
}
93100

94-
let code = Generator::new(&input, &contexts, heritage.as_ref(), MapChain::default())
95-
.build(&contexts[&input.path])?;
101+
let code = Generator::new(
102+
&input,
103+
&contexts,
104+
heritage.as_ref(),
105+
MapChain::default(),
106+
input.block.is_some(),
107+
)
108+
.build(&contexts[&input.path])?;
96109
if input.print == Print::Code || input.print == Print::All {
97110
eprintln!("{code}");
98111
}

testing/tests/block_fragments.rs

+42
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,45 @@ fn test_specific_block() {
103103
let t = RenderInPlace { s1 };
104104
assert_eq!(t.render().unwrap(), "\nSection: [abc]\n");
105105
}
106+
107+
#[derive(Template)]
108+
#[template(
109+
source = r#"{% block empty %}
110+
{% endblock %}
111+
112+
{% if let Some(var) = var %}
113+
{{ var }}
114+
{% endif %}"#,
115+
block = "empty",
116+
ext = "txt"
117+
)]
118+
struct Empty {}
119+
120+
#[test]
121+
fn test_render_only_block() {
122+
assert_eq!(Empty {}.render().unwrap(), "\n");
123+
}
124+
125+
#[derive(Template)]
126+
#[template(
127+
source = r#"{% extends "fragment-base.html" %}
128+
129+
{% block body %}
130+
{% include "included.html" %}
131+
{% endblock %}
132+
133+
{% block other_body %}
134+
<p>Don't render me.</p>
135+
{% endblock %}"#,
136+
block = "body",
137+
ext = "html"
138+
)]
139+
struct FragmentInclude<'a> {
140+
s: &'a str,
141+
}
142+
143+
#[test]
144+
fn test_fragment_include() {
145+
let fragment_include = FragmentInclude { s: "world" };
146+
assert_eq!(fragment_include.render().unwrap(), "\nINCLUDED: world\n");
147+
}

0 commit comments

Comments
 (0)