Skip to content

Commit 3155662

Browse files
committed
Fix HTML shortcodes
Closes #1689
1 parent 5b0e1ed commit 3155662

File tree

3 files changed

+99
-74
lines changed

3 files changed

+99
-74
lines changed

Diff for: components/rendering/src/markdown.rs

+52-73
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,56 @@ pub fn markdown_to_html(
187187

188188
{
189189
let mut events = Vec::new();
190+
macro_rules! render_shortcodes {
191+
($is_text:expr, $text:expr, $range:expr) => {
192+
let orig_range_start = $range.start;
193+
loop {
194+
if let Some(ref shortcode) = next_shortcode {
195+
if !$range.contains(&shortcode.span.start) {
196+
break;
197+
}
198+
let sc_span = shortcode.span.clone();
199+
200+
// we have some text before the shortcode, push that first
201+
if $range.start != sc_span.start {
202+
let content = $text[($range.start - orig_range_start)
203+
..(sc_span.start - orig_range_start)]
204+
.to_string()
205+
.into();
206+
events.push(if $is_text {
207+
Event::Text(content)
208+
} else {
209+
Event::Html(content)
210+
});
211+
$range.start = sc_span.start;
212+
}
213+
214+
// Now we should be at the same idx as the shortcode
215+
let shortcode = next_shortcode.take().unwrap();
216+
match shortcode.render(&context.tera, &context.tera_context) {
217+
Ok(s) => {
218+
events.push(Event::Html(s.into()));
219+
$range.start += SHORTCODE_PLACEHOLDER.len();
220+
}
221+
Err(e) => {
222+
error = Some(e);
223+
break;
224+
}
225+
}
226+
next_shortcode = html_shortcodes.pop();
227+
continue;
228+
}
229+
230+
break;
231+
}
232+
233+
if !$range.is_empty() {
234+
// The $range value is for the whole document, not for this slice of text
235+
let content = $text[($range.start - orig_range_start)..].to_string().into();
236+
events.push(if $is_text { Event::Text(content) } else { Event::Html(content) });
237+
}
238+
};
239+
}
190240

191241
for (event, mut range) in Parser::new_ext(content, opts).into_offset_iter() {
192242
match event {
@@ -206,45 +256,7 @@ pub fn markdown_to_html(
206256
continue;
207257
}
208258

209-
// TODO: find a way to share that code with the HTML handler
210-
let mut new_text = text.clone();
211-
loop {
212-
if let Some(ref shortcode) = next_shortcode {
213-
let sc_span = shortcode.span.clone();
214-
if range.contains(&sc_span.start) {
215-
if range.start != sc_span.start {
216-
events.push(Event::Text(
217-
new_text[..(sc_span.start - range.start)]
218-
.to_string()
219-
.into(),
220-
));
221-
}
222-
223-
let shortcode = next_shortcode.take().unwrap();
224-
225-
match shortcode.render(&context.tera, &context.tera_context) {
226-
Ok(s) => {
227-
events.push(Event::Html(s.into()));
228-
new_text = new_text[(sc_span.end - range.start)..]
229-
.to_owned()
230-
.into();
231-
range.start = sc_span.end - range.start;
232-
}
233-
Err(e) => {
234-
error = Some(e);
235-
break;
236-
}
237-
}
238-
239-
next_shortcode = html_shortcodes.pop();
240-
continue;
241-
}
242-
}
243-
244-
break;
245-
}
246-
247-
events.push(Event::Text(new_text[..].to_string().into()));
259+
render_shortcodes!(true, text, range);
248260
}
249261
}
250262
Event::Start(Tag::CodeBlock(ref kind)) => {
@@ -338,40 +350,7 @@ pub fn markdown_to_html(
338350
continue;
339351
}
340352

341-
let mut new_text = text.clone();
342-
loop {
343-
if let Some(ref shortcode) = next_shortcode {
344-
let sc_span = shortcode.span.clone();
345-
if range.contains(&sc_span.start) {
346-
if range.start != sc_span.start {
347-
events.push(Event::Html(
348-
new_text[..(sc_span.start - range.start)].to_owned().into(),
349-
));
350-
}
351-
352-
let shortcode = next_shortcode.take().unwrap();
353-
match shortcode.render(&context.tera, &context.tera_context) {
354-
Ok(s) => {
355-
events.push(Event::Html(s.into()));
356-
new_text = new_text[(sc_span.end - range.start)..]
357-
.to_owned()
358-
.into();
359-
range.start = sc_span.end - range.start;
360-
}
361-
Err(e) => {
362-
error = Some(e);
363-
break;
364-
}
365-
}
366-
367-
next_shortcode = html_shortcodes.pop();
368-
continue;
369-
}
370-
}
371-
372-
break;
373-
}
374-
events.push(Event::Html(new_text[..].to_string().into()));
353+
render_shortcodes!(false, text, range);
375354
}
376355
_ => events.push(event),
377356
}

Diff for: components/rendering/src/shortcode/parser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::collections::HashMap;
88
use tera::{to_value, Context, Map, Tera, Value};
99
use utils::templates::ShortcodeFileType;
1010

11-
pub const SHORTCODE_PLACEHOLDER: &str = "||ZOLA_SC_PLACEHOLDER||";
11+
pub const SHORTCODE_PLACEHOLDER: &str = "@@ZOLA_SC_PLACEHOLDER@@";
1212

1313
#[derive(PartialEq, Debug)]
1414
pub struct Shortcode {

Diff for: components/rendering/tests/markdown.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1656,3 +1656,49 @@ ttest2
16561656
let res = render_content(markdown_string, &context).unwrap();
16571657
assert_eq!(res.body, "<p>ttest1</p>\n<p>123</p>\n<p>ttest2</p>\n<p>123</p>\n");
16581658
}
1659+
1660+
// https://github.com/getzola/zola/issues/1689
1661+
#[test]
1662+
fn html_shortcode_regression() {
1663+
let permalinks_ctx = HashMap::new();
1664+
let mut tera = Tera::default();
1665+
tera.extend(&ZOLA_TERA).unwrap();
1666+
tera.add_raw_template("shortcodes/ex.html", "1").unwrap();
1667+
tera.add_raw_template("shortcodes/book.html", "2").unwrap();
1668+
tera.add_raw_template("shortcodes/std.html", "3").unwrap();
1669+
let config = Config::default_for_test();
1670+
let mut context = RenderContext::new(
1671+
&tera,
1672+
&config,
1673+
&config.default_language,
1674+
"",
1675+
&permalinks_ctx,
1676+
InsertAnchor::None,
1677+
);
1678+
let shortcode_def = utils::templates::get_shortcodes(&tera);
1679+
context.set_shortcode_definitions(&shortcode_def);
1680+
1681+
let markdown_string = r#"{{ book(page="") }} {{ ex(page="") }} {{ std(page="std") }}"#;
1682+
let res = render_content(markdown_string, &context).unwrap();
1683+
assert_eq!(res.body, "<p>2 1 3</p>\n");
1684+
1685+
// And in html
1686+
let markdown_string = r#"<p>{{ book(page="") }} {{ ex(page="") }} {{ std(page="std") }}</p>"#;
1687+
let res = render_content(markdown_string, &context).unwrap();
1688+
assert_eq!(res.body, "<p>2 1 3</p>");
1689+
1690+
// Another one with newlines
1691+
let markdown_string = "<p>\n{{ book(page='') }}\n</p>";
1692+
let res = render_content(markdown_string, &context).unwrap();
1693+
assert_eq!(res.body, "<p>\n2\n</p>");
1694+
1695+
// And another one
1696+
let markdown_string = "<span>{{ book(page='') }}</span>\n**The Book** {{ book(page='') }}";
1697+
let res = render_content(markdown_string, &context).unwrap();
1698+
assert_eq!(res.body, "<p><span>2</span>\n<strong>The Book</strong> 2</p>\n");
1699+
1700+
// with some text in between
1701+
let markdown_string = r#"a.{{ book(page="") }} b.{{ ex(page="") }} c.{{ std(page="std") }}"#;
1702+
let res = render_content(markdown_string, &context).unwrap();
1703+
assert_eq!(res.body, "<p>a.2 b.1 c.3</p>\n");
1704+
}

0 commit comments

Comments
 (0)