From bc511e0266bdbeb56c1524dcae7d54e3a01e07ce Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 15 Jun 2024 14:57:59 +0200 Subject: [PATCH 1/3] use Cow for indent --- src/partial.rs | 3 ++- src/render.rs | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/partial.rs b/src/partial.rs index d1cc7afab..74f168c17 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -133,11 +133,12 @@ pub fn expand_partial<'reg: 'rc, 'rc>( } // indent - local_rc.set_indent_string(d.indent()); + local_rc.set_indent_string(d.indent().cloned()); let result = t.render(r, ctx, &mut local_rc, out); // cleanup + if block_created { local_rc.pop_block(); } diff --git a/src/render.rs b/src/render.rs index 96cbdb7e3..789f01aa2 100644 --- a/src/render.rs +++ b/src/render.rs @@ -48,7 +48,7 @@ pub struct RenderContextInner<'reg: 'rc, 'rc> { /// root template name root_template: Option<&'reg String>, disable_escape: bool, - indent_string: Option<&'reg String>, + indent_string: Option>, } impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> { @@ -202,13 +202,13 @@ impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> { } } - pub(crate) fn set_indent_string(&mut self, indent: Option<&'reg String>) { + pub(crate) fn set_indent_string(&mut self, indent: Option>) { self.inner_mut().indent_string = indent; } #[inline] - pub(crate) fn get_indent_string(&self) -> Option<&'reg String> { - self.inner.indent_string + pub(crate) fn get_indent_string(&self) -> Option<&Cow<'reg, str>> { + self.inner.indent_string.as_ref() } /// Remove a registered partial @@ -454,7 +454,7 @@ pub struct Decorator<'rc> { params: Vec>, hash: BTreeMap<&'rc str, PathAndJson<'rc>>, template: Option<&'rc Template>, - indent: Option<&'rc String>, + indent: Option>, } impl<'reg: 'rc, 'rc> Decorator<'rc> { @@ -478,12 +478,23 @@ impl<'reg: 'rc, 'rc> Decorator<'rc> { hm.insert(k.as_ref(), r); } + let indent = match (render_context.get_indent_string(), dt.indent.as_ref()) { + (None, None) => None, + (Some(s), None) => Some(s.clone()), + (None, Some(s)) => Some(Cow::Borrowed(&**s)), + (Some(s1), Some(s2)) => { + let mut res = s1.to_string(); + res.push_str(s2); + Some(Cow::from(res)) + } + }; + Ok(Decorator { name, params: pv, hash: hm, template: dt.template.as_ref(), - indent: dt.indent.as_ref(), + indent, }) } @@ -517,8 +528,8 @@ impl<'reg: 'rc, 'rc> Decorator<'rc> { self.template } - pub fn indent(&self) -> Option<&'rc String> { - self.indent + pub fn indent(&self) -> Option<&Cow<'rc, str>> { + self.indent.as_ref() } } From 443a49358f74c103a8d4abf375fbc59d82fce806 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 15 Jun 2024 14:55:47 +0200 Subject: [PATCH 2/3] add TemplateElement::Indent --- src/render.rs | 6 ++++++ src/template.rs | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/render.rs b/src/render.rs index 789f01aa2..750918f28 100644 --- a/src/render.rs +++ b/src/render.rs @@ -807,6 +807,12 @@ impl Renderable for TemplateElement { out: &mut dyn Output, ) -> Result<(), RenderError> { match self { + Indent => { + if let Some(indent) = rc.get_indent_string() { + out.write(indent)?; + } + Ok(()) + } RawString(ref v) => indent_aware_write(v.as_ref(), rc, out), Expression(ref ht) | HtmlExpression(ref ht) => { let is_html_expression = matches!(self, HtmlExpression(_)); diff --git a/src/template.rs b/src/template.rs index 065661cd8..55f38fc66 100644 --- a/src/template.rs +++ b/src/template.rs @@ -634,6 +634,7 @@ impl Template { // this option is marked as true when standalone statement is detected // then the leading whitespaces and newline of next rawstring will be trimed let mut trim_line_required = false; + let mut prev_rule = None; let parser_queue = HandlebarsParser::parse(Rule::handlebars, source).map_err(|e| { let (line_no, col_no) = match e.line_col { @@ -718,6 +719,14 @@ impl Template { }; let t = template_stack.front_mut().unwrap(); + + // If this text element is following a standalone partial, then + // we trim the whitespace between. But we still want the following text + // to be indented correctly, so we insert the special `Indent` element. + if trim_line_required && prev_rule == Some(Rule::partial_expression) { + t.push_element(TemplateElement::Indent, line_no, col_no); + } + t.push_element( Template::raw_string( &source[start..span.end()], @@ -976,6 +985,7 @@ impl Template { if rule != Rule::template { end_pos = Some(span.end_pos()); } + prev_rule = Some(rule); } else { let prev_end = end_pos.as_ref().map(|e| e.pos()).unwrap_or(0); if prev_end < source.len() { @@ -1016,6 +1026,7 @@ impl Template { #[derive(PartialEq, Eq, Clone, Debug)] pub enum TemplateElement { + Indent, RawString(String), HtmlExpression(Box), Expression(Box), From 18eeb6c769af5c8a7ba15fb558baefba9d9ea86d Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 15 Jun 2024 15:05:35 +0200 Subject: [PATCH 3/3] add testcase for nested partials --- src/partial.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/partial.rs b/src/partial.rs index 74f168c17..9a265f8d1 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -666,6 +666,48 @@ outer third line"#, ) } + #[test] + fn test_indent_level_on_nested_partials() { + let nested_partial = " +
+ content +
+"; + let partial = " +
+ {{>nested_partial}} +
+"; + + let partial_indented = " +
+ {{>partial}} +
+"; + + let result = " +
+
+
+ content +
+
+
+"; + + let mut hb = Registry::new(); + hb.register_template_string("nested_partial", nested_partial.trim_start()) + .unwrap(); + hb.register_template_string("partial", partial.trim_start()) + .unwrap(); + hb.register_template_string("partial_indented", partial_indented.trim_start()) + .unwrap(); + + let s = hb.render("partial_indented", &()).unwrap(); + + assert_eq!(&s, result.trim_start()); + } + #[test] fn test_issue_534() { let t1 = "{{title}}";