Add shortcode 'invocation' variable to allow a shortcode to track how… (#1236)
* add shortcode 'invocation' variable to allow a shortcode to track how many times it has been invoked in a given Markdown file * use closure (implicit struct) instead of explicit struct for invocation tracking * update variable name to "nth"
This commit is contained in:
parent
a93063ba4a
commit
a210abc5a3
|
@ -3,6 +3,7 @@ use pest::iterators::Pair;
|
|||
use pest::Parser;
|
||||
use pest_derive::Parser;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use tera::{to_value, Context, Map, Value};
|
||||
|
||||
use crate::context::RenderContext;
|
||||
|
@ -102,6 +103,7 @@ fn render_shortcode(
|
|||
name: &str,
|
||||
args: &Map<String, Value>,
|
||||
context: &RenderContext,
|
||||
invocation_count: u32,
|
||||
body: Option<&str>,
|
||||
) -> Result<String> {
|
||||
let mut tera_context = Context::new();
|
||||
|
@ -112,6 +114,7 @@ fn render_shortcode(
|
|||
// Trimming right to avoid most shortcodes with bodies ending up with a HTML new line
|
||||
tera_context.insert("body", b.trim_end());
|
||||
}
|
||||
tera_context.insert("nth", &invocation_count);
|
||||
tera_context.extend(context.tera_context.clone());
|
||||
|
||||
let mut template_name = format!("shortcodes/{}.md", name);
|
||||
|
@ -139,6 +142,12 @@ fn render_shortcode(
|
|||
|
||||
pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<String> {
|
||||
let mut res = String::with_capacity(content.len());
|
||||
let mut invocation_map: HashMap<String, u32> = HashMap::new();
|
||||
let mut get_invocation_count = |name: &str| {
|
||||
let invocation_number = invocation_map.entry(String::from(name)).or_insert(0);
|
||||
*invocation_number += 1;
|
||||
*invocation_number
|
||||
};
|
||||
|
||||
let mut pairs = match ContentParser::parse(Rule::page, content) {
|
||||
Ok(p) => p,
|
||||
|
@ -184,7 +193,13 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<Strin
|
|||
Rule::text => res.push_str(p.as_span().as_str()),
|
||||
Rule::inline_shortcode => {
|
||||
let (name, args) = parse_shortcode_call(p);
|
||||
res.push_str(&render_shortcode(&name, &args, context, None)?);
|
||||
res.push_str(&render_shortcode(
|
||||
&name,
|
||||
&args,
|
||||
context,
|
||||
get_invocation_count(&name),
|
||||
None,
|
||||
)?);
|
||||
}
|
||||
Rule::shortcode_with_body => {
|
||||
let mut inner = p.into_inner();
|
||||
|
@ -192,7 +207,13 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<Strin
|
|||
// we don't care about the closing tag
|
||||
let (name, args) = parse_shortcode_call(inner.next().unwrap());
|
||||
let body = inner.next().unwrap().as_span().as_str();
|
||||
res.push_str(&render_shortcode(&name, &args, context, Some(body))?);
|
||||
res.push_str(&render_shortcode(
|
||||
&name,
|
||||
&args,
|
||||
context,
|
||||
get_invocation_count(&name),
|
||||
Some(body),
|
||||
)?);
|
||||
}
|
||||
Rule::ignored_inline_shortcode => {
|
||||
res.push_str(
|
||||
|
|
|
@ -1059,6 +1059,36 @@ fn emoji_aliases_are_ignored_when_disabled_in_config() {
|
|||
assert_eq!(res.body, "<p>Hello, World! :smile:</p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invocation_count_increments_in_shortcode() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
|
||||
let shortcode_template_a = r#"<p>a: {{ nth }}</p>"#;
|
||||
let shortcode_template_b = r#"<p>b: {{ nth }}</p>"#;
|
||||
|
||||
let markdown_string = r#"{{ a() }}
|
||||
{{ b() }}
|
||||
{{ a() }}
|
||||
{{ b() }}
|
||||
"#;
|
||||
|
||||
let expected = r#"<p>a: 1</p>
|
||||
<p>b: 1</p>
|
||||
<p>a: 2</p>
|
||||
<p>b: 2</p>
|
||||
"#;
|
||||
|
||||
tera.add_raw_template("shortcodes/a.html", shortcode_template_a).unwrap();
|
||||
tera.add_raw_template("shortcodes/b.html", shortcode_template_b).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_external_links_unchanged() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
|
|
|
@ -134,6 +134,24 @@ If you want to have some content that looks like a shortcode but not have Zola t
|
|||
you will need to escape it by using `{%/*` and `*/%}` instead of `{%` and `%}`. You won't need to escape
|
||||
anything else until the closing tag.
|
||||
|
||||
### Invocation Count
|
||||
|
||||
Every shortcode context is passed in a variable named `nth` that tracks how many times a particular shortcode has
|
||||
been invoked in a Markdown file. Given a shortcode `true_statement.html` template:
|
||||
|
||||
```jinja2
|
||||
<p id="number{{ nth }}">{{ value }} is equal to {{ nth }}.</p>
|
||||
```
|
||||
|
||||
It could be used in our Markdown as follows:
|
||||
|
||||
```md
|
||||
{{/* true_statement(value=1) */}}
|
||||
{{/* true_statement(value=2) */}}
|
||||
```
|
||||
|
||||
This is useful when implementing custom markup for features such as sidenotes or end notes.
|
||||
|
||||
## Built-in shortcodes
|
||||
|
||||
Zola comes with a few built-in shortcodes. If you want to override a default shortcode template,
|
||||
|
|
Loading…
Reference in a new issue