diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index 9e15ca4251..56f229ec0e 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -187,6 +187,56 @@ pub fn markdown_to_html( { let mut events = Vec::new(); + macro_rules! render_shortcodes { + ($is_text:expr, $text:expr, $range:expr) => { + let orig_range_start = $range.start; + loop { + if let Some(ref shortcode) = next_shortcode { + if !$range.contains(&shortcode.span.start) { + break; + } + let sc_span = shortcode.span.clone(); + + // we have some text before the shortcode, push that first + if $range.start != sc_span.start { + let content = $text[($range.start - orig_range_start) + ..(sc_span.start - orig_range_start)] + .to_string() + .into(); + events.push(if $is_text { + Event::Text(content) + } else { + Event::Html(content) + }); + $range.start = sc_span.start; + } + + // Now we should be at the same idx as the shortcode + let shortcode = next_shortcode.take().unwrap(); + match shortcode.render(&context.tera, &context.tera_context) { + Ok(s) => { + events.push(Event::Html(s.into())); + $range.start += SHORTCODE_PLACEHOLDER.len(); + } + Err(e) => { + error = Some(e); + break; + } + } + next_shortcode = html_shortcodes.pop(); + continue; + } + + break; + } + + if !$range.is_empty() { + // The $range value is for the whole document, not for this slice of text + let content = $text[($range.start - orig_range_start)..].to_string().into(); + events.push(if $is_text { Event::Text(content) } else { Event::Html(content) }); + } + }; + } for (event, mut range) in Parser::new_ext(content, opts).into_offset_iter() { match event { @@ -206,45 +256,7 @@ pub fn markdown_to_html( continue; } - // TODO: find a way to share that code with the HTML handler - let mut new_text = text.clone(); - loop { - if let Some(ref shortcode) = next_shortcode { - let sc_span = shortcode.span.clone(); - if range.contains(&sc_span.start) { - if range.start != sc_span.start { - events.push(Event::Text( - new_text[..(sc_span.start - range.start)] - .to_string() - .into(), - )); - } - - let shortcode = next_shortcode.take().unwrap(); - - match shortcode.render(&context.tera, &context.tera_context) { - Ok(s) => { - events.push(Event::Html(s.into())); - new_text = new_text[(sc_span.end - range.start)..] - .to_owned() - .into(); - range.start = sc_span.end - range.start; - } - Err(e) => { - error = Some(e); - break; - } - } - - next_shortcode = html_shortcodes.pop(); - continue; - } - } - - break; - } - - events.push(Event::Text(new_text[..].to_string().into())); + render_shortcodes!(true, text, range); } } Event::Start(Tag::CodeBlock(ref kind)) => { @@ -338,40 +350,7 @@ pub fn markdown_to_html( continue; } - let mut new_text = text.clone(); - loop { - if let Some(ref shortcode) = next_shortcode { - let sc_span = shortcode.span.clone(); - if range.contains(&sc_span.start) { - if range.start != sc_span.start { - events.push(Event::Html( - new_text[..(sc_span.start - range.start)].to_owned().into(), - )); - } - - let shortcode = next_shortcode.take().unwrap(); - match shortcode.render(&context.tera, &context.tera_context) { - Ok(s) => { - events.push(Event::Html(s.into())); - new_text = new_text[(sc_span.end - range.start)..] - .to_owned() - .into(); - range.start = sc_span.end - range.start; - } - Err(e) => { - error = Some(e); - break; - } - } - - next_shortcode = html_shortcodes.pop(); - continue; - } - } - - break; - } - events.push(Event::Html(new_text[..].to_string().into())); + render_shortcodes!(false, text, range); } _ => events.push(event), } diff --git a/components/rendering/tests/markdown.rs b/components/rendering/tests/markdown.rs index f158a8e254..bcd0eda297 100644 --- a/components/rendering/tests/markdown.rs +++ b/components/rendering/tests/markdown.rs @@ -1656,3 +1656,49 @@ ttest2 let res = render_content(markdown_string, &context).unwrap(); assert_eq!(res.body, "

ttest1

\n

123

\n

ttest2

\n

123

\n"); } + +// https://github.com/getzola/zola/issues/1689 +#[test] +fn html_shortcode_regression() { + let permalinks_ctx = HashMap::new(); + let mut tera = Tera::default(); + tera.extend(&ZOLA_TERA).unwrap(); + tera.add_raw_template("shortcodes/ex.html", "1").unwrap(); + tera.add_raw_template("shortcodes/book.html", "2").unwrap(); + tera.add_raw_template("shortcodes/std.html", "3").unwrap(); + let config = Config::default_for_test(); + let mut context = RenderContext::new( + &tera, + &config, + &config.default_language, + "", + &permalinks_ctx, + InsertAnchor::None, + ); + let shortcode_def = utils::templates::get_shortcodes(&tera); + context.set_shortcode_definitions(&shortcode_def); + + let markdown_string = r#"{{ book(page="") }} {{ ex(page="") }} {{ std(page="std") }}"#; + let res = render_content(markdown_string, &context).unwrap(); + assert_eq!(res.body, "

2 1 3

\n"); + + // And in html + let markdown_string = r#"

{{ book(page="") }} {{ ex(page="") }} {{ std(page="std") }}

"#; + let res = render_content(markdown_string, &context).unwrap(); + assert_eq!(res.body, "

2 1 3

"); + + // Another one with newlines + let markdown_string = "

\n{{ book(page='') }}\n

"; + let res = render_content(markdown_string, &context).unwrap(); + assert_eq!(res.body, "

\n2\n

"); + + // And another one + let markdown_string = "{{ book(page='') }}\n**The Book** {{ book(page='') }}"; + let res = render_content(markdown_string, &context).unwrap(); + assert_eq!(res.body, "

2\nThe Book 2

\n"); + + // with some text in between + let markdown_string = r#"a.{{ book(page="") }} b.{{ ex(page="") }} c.{{ std(page="std") }}"#; + let res = render_content(markdown_string, &context).unwrap(); + assert_eq!(res.body, "

a.2 b.1 c.3

\n"); +}