diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index 56f229ec..4181c2dc 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -242,7 +242,32 @@ pub fn markdown_to_html( match event { Event::Text(text) => { if let Some(ref mut code_block) = code_block { - let html = code_block.highlight(&text); + let html; + if contains_shortcode(text.as_ref()) { + let mut accumulated_block = String::new(); + // mark the start of the code block events + let stack_start = events.len(); + render_shortcodes!(true, text, range); + // after rendering the shortcodes we will collect all the text events + // and re-render them as code blocks + for event in events[stack_start..].iter() { + match event { + Event::Html(t) | Event::Text(t) => accumulated_block += t, + _ => { + error = Some(Error::msg(format!( + "Unexpected event while expanding the code block: {:?}", + event + ))); + break; + } + } + } + html = code_block.highlight(&accumulated_block); + // remove all the original events from shortcode rendering + events.truncate(stack_start); + } else { + html = code_block.highlight(&text); + } events.push(Event::Html(html.into())); } else { let text = if context.config.markdown.render_emoji { diff --git a/components/rendering/tests/codeblock_shortcode_mix.rs b/components/rendering/tests/codeblock_shortcode_mix.rs new file mode 100644 index 00000000..e1781913 --- /dev/null +++ b/components/rendering/tests/codeblock_shortcode_mix.rs @@ -0,0 +1,271 @@ +use std::collections::HashMap; + +use config::Config; +use front_matter::InsertAnchor; +use templates::ZOLA_TERA; +use rendering::{render_content, RenderContext}; + +#[test] +fn can_render_shortcode_in_codeblock() { + let permalinks_ctx = HashMap::new(); + let config = Config::default_for_test(); + let mut context = RenderContext::new( + &ZOLA_TERA, + &config, + &config.default_language, + "", + &permalinks_ctx, + InsertAnchor::None, + ); + let shortcode_def = utils::templates::get_shortcodes(&ZOLA_TERA); + context.set_shortcode_definitions(&shortcode_def); + // simple case + let res = render_content( + r#" +``` +{{ youtube(id="dQw4w9WgXcQ") }} +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
<div >\n    <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n</div>\n\n
\n" + ); + // mixed with other contents + let res = render_content( + r#" +``` +
+{{ youtube(id="dQw4w9WgXcQ") }} +
+``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
<div id="custom-attr">\n<div >\n    <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n</div>\n\n</div>\n
\n" + ); + // mixed content with syntax and line numbers + let res = render_content( + r#" +```html,linenos +
+{{ youtube(id="dQw4w9WgXcQ") }} +
+``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
1<div id="custom-attr">\n
2<div >\n
3 <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n
4</div>\n
5\n
6</div>\n
\n" + ); +} + +#[test] +fn can_render_multiple_shortcodes_in_codeblock() { + let permalinks_ctx = HashMap::new(); + let config = Config::default_for_test(); + let mut context = RenderContext::new( + &ZOLA_TERA, + &config, + &config.default_language, + "", + &permalinks_ctx, + InsertAnchor::None, + ); + let shortcode_def = utils::templates::get_shortcodes(&ZOLA_TERA); + context.set_shortcode_definitions(&shortcode_def); + // simple case + let res = render_content( + r#" +``` +{{ youtube(id="dQw4w9WgXcQ") }} +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
<div >\n    <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n</div>\n\n<div class="gist">\n    <script src="https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;e5fb6aad409f28721c0ba14161644c57.js"></script>\n</div>\n\n
\n" + ); + // mixed with other contents + let res = render_content( + r#" +``` +text 1 +{{ youtube(id="dQw4w9WgXcQ") }} +text 2 +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +text 3 +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
text 1\n<div >\n    <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n</div>\n\ntext 2\n<div class="gist">\n    <script src="https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;e5fb6aad409f28721c0ba14161644c57.js"></script>\n</div>\n\ntext 3\n
\n" + ); + // mixed content with syntax and line numbers + let res = render_content( + r#" +```html,linenos +text 1 +{{ youtube(id="dQw4w9WgXcQ") }} +text 2 +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +text 3 +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, +r#"
1<span>text 1</span> +
2<div > +
3 <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> +
4</div> +
5 +
6<span>text 2</span> +
7<div class="gist"> +
8 <script src="https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;e5fb6aad409f28721c0ba14161644c57.js"></script> +
9</div> +
10 +
11<span>text 3</span> +
+"# + ); +} + +#[test] +fn is_highlighting_linenos_still_working() { + let permalinks_ctx = HashMap::new(); + let mut config = Config::default_for_test(); + config.markdown.highlight_code = true; + let mut context = RenderContext::new( + &ZOLA_TERA, + &config, + &config.default_language, + "", + &permalinks_ctx, + InsertAnchor::None, + ); + let shortcode_def = utils::templates::get_shortcodes(&ZOLA_TERA); + context.set_shortcode_definitions(&shortcode_def); + // single shortcode mixed with syntax and line numbers + let res = render_content( + r#" +```html,linenos +
+{{ youtube(id="dQw4w9WgXcQ") }} +
+``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, + "
1<div id="custom-attr">\n
2<div >\n
3 <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n
4</div>\n
5\n
6</div>\n
\n" + ); + // multiple shortcode mixed with syntax and line numbers + let res = render_content( + r#" +```html,linenos +text 1 +{{ youtube(id="dQw4w9WgXcQ") }} +text 2 +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +text 3 +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, +r#"
1<span>text 1</span> +
2<div > +
3 <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> +
4</div> +
5 +
6<span>text 2</span> +
7<div class="gist"> +
8 <script src="https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;e5fb6aad409f28721c0ba14161644c57.js"></script> +
9</div> +
10 +
11<span>text 3</span> +
+"# + ); +} + +#[test] +fn codeblock_shortcode_mix_all_stars() { + let permalinks_ctx = HashMap::new(); + let mut config = Config::default_for_test(); + config.markdown.highlight_code = true; + let mut context = RenderContext::new( + &ZOLA_TERA, + &config, + &config.default_language, + "", + &permalinks_ctx, + InsertAnchor::None, + ); + let shortcode_def = utils::templates::get_shortcodes(&ZOLA_TERA); + context.set_shortcode_definitions(&shortcode_def); + // single shortcode mixed with syntax and line numbers + let res = render_content( + r#" +```html,linenos +{{/* before(texts="1") */}} +Normally people would not write something & like <> this: +
+An inline {{ youtube(id="dQw4w9WgXcQ", autoplay=true, class="youtube") }} shortcode +
+Plain text in-between +{%/* quote(author="Vincent") */%} +A quote +{%/* end */%} +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +{# A Tera comment, you should see it #} + +``` + "#, + &context, + ) + .unwrap(); + assert_eq!( + res.body, +r#"
1<a href="javascript:void(0);">{{ before(texts="1") }}</a> +
2Normally people would not write something & like <> this: +
3<div id="custom-attr"> +
4An inline <div class="youtube"> +
5 <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ?autoplay=1" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> +
6</div> +
7 shortcode +
8</div> +
9Plain text in-between +
10{% quote(author="Vincent") %} +
11A quote +
12{% end %} +
13<div class="gist"> +
14 <script src="https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;e5fb6aad409f28721c0ba14161644c57.js"></script> +
15</div> +
16 +
17{# A Tera comment, you should see it #} +
18<!-- end text goes here --> +
+"# + ); +} \ No newline at end of file