From b0159cca78e33da634dc255b7b4f1be99c3f5218 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Thu, 9 Dec 2021 21:00:48 +0100 Subject: [PATCH] Fix HTML shortcodes Closes #1689 --- components/rendering/src/markdown.rs | 125 ++++++++----------- components/rendering/src/shortcode/parser.rs | 2 +- components/rendering/tests/markdown.rs | 46 +++++++ 3 files changed, 99 insertions(+), 74 deletions(-) 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/src/shortcode/parser.rs b/components/rendering/src/shortcode/parser.rs index a2478f0be4..bcb14116bc 100644 --- a/components/rendering/src/shortcode/parser.rs +++ b/components/rendering/src/shortcode/parser.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use tera::{to_value, Context, Map, Tera, Value}; use utils::templates::ShortcodeFileType; -pub const SHORTCODE_PLACEHOLDER: &str = "||ZOLA_SC_PLACEHOLDER||"; +pub const SHORTCODE_PLACEHOLDER: &str = "@@ZOLA_SC_PLACEHOLDER@@"; #[derive(PartialEq, Debug)] pub struct Shortcode { 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"); +}