diff --git a/src/render.rs b/src/render.rs index 637dc36b7..cce54e8f7 100644 --- a/src/render.rs +++ b/src/render.rs @@ -52,10 +52,16 @@ pub struct RenderContextInner<'reg: 'rc, 'rc> { /// root template name root_template: Option<&'reg String>, disable_escape: bool, - // whether the previous text that we rendered ended on a newline - // necessary to make indenting decisions after the end of partials + + // Indicates whether the previous text that we rendered ended on a newline. + // This is necessary to make indenting decisions after the end of partials. trailing_newline: bool, - // whether the previous text that we render should indent itself + + // This should be set to true whenever any output is written. + // We need this to detect empty partials/helpers for indenting decisions. + content_produced: bool, + + // The next text that we render should indent itself. indent_before_write: bool, indent_string: Option>, } @@ -72,6 +78,7 @@ impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> { root_template, disable_escape: false, trailing_newline: false, + content_produced: false, indent_before_write: false, indent_string: None, }); @@ -304,6 +311,16 @@ impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> { self.inner().trailing_newline } + #[inline] + pub fn set_content_produced(&mut self, content_produced: bool) { + self.inner_mut().content_produced = content_produced; + } + + #[inline] + pub fn get_content_produced(&self) -> bool { + self.inner().content_produced + } + #[inline] pub fn set_indent_before_write(&mut self, indent_before_write: bool) { self.inner_mut().indent_before_write = indent_before_write; @@ -786,9 +803,21 @@ fn render_helper<'reg: 'rc, 'rc>( h.hash() ); let mut call_indent_aware = |helper_def: &dyn HelperDef, rc: &mut RenderContext<'reg, 'rc>| { - rc.set_indent_before_write(ht.indent_before_write && rc.get_trailine_newline()); + let indent_directive_before = rc.get_indent_before_write(); + let content_produced_before = rc.get_content_produced(); + rc.set_content_produced(false); + rc.set_indent_before_write( + indent_directive_before || (ht.indent_before_write && rc.get_trailine_newline()), + ); + helper_def.call(&h, registry, ctx, rc, out)?; - rc.set_indent_before_write(rc.get_trailine_newline()); + + if rc.get_content_produced() { + rc.set_indent_before_write(rc.get_trailine_newline()); + } else { + rc.set_content_produced(content_produced_before); + rc.set_indent_before_write(indent_directive_before); + } Ok(()) }; if let Some(ref d) = rc.get_local_helper(h.name()) { @@ -827,6 +856,7 @@ pub fn indent_aware_write( if v.is_empty() { return Ok(()); } + rc.set_content_produced(true); if !v.starts_with(newline_matcher) && rc.get_indent_before_write() { if let Some(indent) = rc.get_indent_string() { @@ -904,11 +934,23 @@ impl Renderable for TemplateElement { DecoratorExpression(_) | DecoratorBlock(_) => self.eval(registry, ctx, rc), PartialExpression(ref dt) | PartialBlock(ref dt) => { let di = Decorator::try_from_template(dt, registry, ctx, rc)?; + + let indent_directive_before = rc.get_indent_before_write(); + let content_produced_before = rc.get_content_produced(); + rc.set_indent_before_write( dt.indent_before_write && (rc.get_trailine_newline() || dt.indent.is_some()), ); + rc.set_content_produced(false); + partial::expand_partial(&di, registry, ctx, rc, out)?; - rc.set_indent_before_write(rc.get_trailine_newline()); + + if rc.get_content_produced() { + rc.set_indent_before_write(rc.get_trailine_newline()); + } else { + rc.set_content_produced(content_produced_before); + rc.set_indent_before_write(indent_directive_before); + } Ok(()) } _ => Ok(()), diff --git a/tests/whitespace.rs b/tests/whitespace.rs index 70a2ffc57..de6233f7f 100644 --- a/tests/whitespace.rs +++ b/tests/whitespace.rs @@ -271,6 +271,36 @@ foo ); } +#[test] +fn test_empty_inline_partials_and_helpers_retain_indent_directive() { + let input = r#" +{{~#*inline "empty_partial"}}{{/inline}} + +{{~#*inline "indented_partial"}} +{{>empty_partial}}{{#if true}}{{>empty_partial}}{{/if}}foo +{{/inline}} + {{>indented_partial}} +"#; + let output = " foo\n"; + let hbs = Handlebars::new(); + + assert_eq!(hbs.render_template(input, &()).unwrap(), output); +} + +#[test] +fn test_indent_directive_propagated_but_not_restored_if_content_was_written() { + let input = r#" +{{~#*inline "indented_partial"}} +{{#if true}}{{/if}}{{#if true}}foo{{/if}}foo +{{/inline}} + {{>indented_partial}} +"#; + let output = " foofoo\n"; + let hbs = Handlebars::new(); + + assert_eq!(hbs.render_template(input, &()).unwrap(), output); +} + //regression test for #611 #[test] fn tag_before_eof_becomes_standalone_in_full_template() {