Fix HTML shortcodes

Closes #1689
This commit is contained in:
Vincent Prouillet 2021-12-09 21:00:48 +01:00
parent 5b0e1edebf
commit 3155662db2
3 changed files with 99 additions and 74 deletions

View file

@ -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),
}

View file

@ -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 {

View file

@ -1656,3 +1656,49 @@ ttest2
let res = render_content(markdown_string, &context).unwrap();
assert_eq!(res.body, "<p>ttest1</p>\n<p>123</p>\n<p>ttest2</p>\n<p>123</p>\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, "<p>2 1 3</p>\n");
// And in html
let markdown_string = r#"<p>{{ book(page="") }} {{ ex(page="") }} {{ std(page="std") }}</p>"#;
let res = render_content(markdown_string, &context).unwrap();
assert_eq!(res.body, "<p>2 1 3</p>");
// Another one with newlines
let markdown_string = "<p>\n{{ book(page='') }}\n</p>";
let res = render_content(markdown_string, &context).unwrap();
assert_eq!(res.body, "<p>\n2\n</p>");
// And another one
let markdown_string = "<span>{{ book(page='') }}</span>\n**The Book** {{ book(page='') }}";
let res = render_content(markdown_string, &context).unwrap();
assert_eq!(res.body, "<p><span>2</span>\n<strong>The Book</strong> 2</p>\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, "<p>a.2 b.1 c.3</p>\n");
}