Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix shortcode/continue-reading parsing with inline HTML (smaller version) #2606

Merged
merged 2 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions components/content/src/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ static RFC3339_DATE: Lazy<Regex> = Lazy::new(|| {
).unwrap()
});

static FOOTNOTES_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r#"<sup class="footnote-reference"><a href=\s*.*?>\s*.*?</a></sup>"#).unwrap()
});

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Page {
/// All info about the actual file
Expand Down Expand Up @@ -232,10 +228,7 @@ impl Page {
let res = render_content(&self.raw_content, &context)
.with_context(|| format!("Failed to render content of {}", self.file.path.display()))?;

self.summary = res
.summary_len
.map(|l| &res.body[0..l])
.map(|s| FOOTNOTES_RE.replace_all(s, "").into_owned());
self.summary = res.summary;
self.content = res.body;
self.toc = res.toc;
self.external_links = res.external_links;
Expand Down
54 changes: 36 additions & 18 deletions components/markdown/src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ static MORE_DIVIDER_RE: Lazy<Regex> = Lazy::new(|| {
.unwrap()
});

static FOOTNOTES_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r#"<sup class="footnote-reference"><a href=\s*.*?>\s*.*?</a></sup>"#).unwrap()
});

/// Although there exists [a list of registered URI schemes][uri-schemes], a link may use arbitrary,
/// private schemes. This regex checks if the given string starts with something that just looks
/// like a scheme, i.e., a case-insensitive identifier followed by a colon.
Expand Down Expand Up @@ -78,7 +82,7 @@ fn is_colocated_asset_link(link: &str) -> bool {
#[derive(Debug)]
pub struct Rendered {
pub body: String,
pub summary_len: Option<usize>,
pub summary: Option<String>,
pub toc: Vec<Heading>,
/// Links to site-local pages: relative path plus optional anchor target.
pub internal_links: Vec<(String, Option<String>)>,
Expand Down Expand Up @@ -405,6 +409,7 @@ pub fn markdown_to_html(
.map(|x| x.as_object().unwrap().get("relative_path").unwrap().as_str().unwrap());
// the rendered html
let mut html = String::with_capacity(content.len());
let mut summary = None;
// Set while parsing
let mut error = None;

Expand Down Expand Up @@ -679,17 +684,13 @@ pub fn markdown_to_html(
event
});
}
Event::Html(text) => {
if !has_summary && MORE_DIVIDER_RE.is_match(&text) {
has_summary = true;
events.push(Event::Html(CONTINUE_READING.into()));
continue;
}
if !contains_shortcode(text.as_ref()) {
events.push(Event::Html(text));
continue;
}

Event::Html(text) if !has_summary && MORE_DIVIDER_RE.is_match(text.as_ref()) => {
has_summary = true;
events.push(Event::Html(CONTINUE_READING.into()));
}
Event::Html(text) | Event::InlineHtml(text)
if contains_shortcode(text.as_ref()) =>
{
render_shortcodes!(false, text, range);
}
_ => events.push(event),
Expand Down Expand Up @@ -781,14 +782,31 @@ pub fn markdown_to_html(
convert_footnotes_to_github_style(&mut events);
}

cmark::html::push_html(&mut html, events.into_iter());
let continue_reading = events
.iter()
.position(|e| matches!(e, Event::Html(CowStr::Borrowed(CONTINUE_READING))))
.unwrap_or(events.len());

let mut events = events.into_iter();

// emit everything up to summary
cmark::html::push_html(&mut html, events.by_ref().take(continue_reading));

if has_summary {
// remove footnotes
let summary_html = FOOTNOTES_RE.replace_all(&html, "").into_owned();
summary = Some(summary_html)
}

// emit everything after summary
cmark::html::push_html(&mut html, events);
}

if let Some(e) = error {
Err(e)
} else {
Ok(Rendered {
summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
summary,
body: html,
toc: make_table_of_contents(headings),
internal_links,
Expand Down Expand Up @@ -861,10 +879,10 @@ mod tests {
for more in mores {
let content = format!("{top}\n\n{more}\n\n{bottom}");
let rendered = markdown_to_html(&content, &context, vec![]).unwrap();
assert!(rendered.summary_len.is_some(), "no summary when splitting on {more}");
let summary_len = rendered.summary_len.unwrap();
let summary = &rendered.body[..summary_len].trim();
let body = &rendered.body[summary_len..].trim();
assert!(rendered.summary.is_some(), "no summary when splitting on {more}");
let summary = rendered.summary.unwrap();
let summary = summary.trim();
let body = rendered.body[summary.len()..].trim();
let continue_reading = &body[..CONTINUE_READING.len()];
let body = &body[CONTINUE_READING.len()..].trim();
assert_eq!(summary, &top_rendered);
Expand Down
12 changes: 12 additions & 0 deletions components/markdown/tests/shortcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,15 @@ fn can_use_shortcodes_in_quotes() {
.body;
insta::assert_snapshot!(body);
}

#[test]
fn can_render_with_inline_html() {
let body = common::render(
r#"
Here is <span>{{ ex1(page="") }}</span> example.
"#,
)
.unwrap()
.body;
insta::assert_snapshot!(body);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: components/markdown/tests/shortcodes.rs
expression: body
---
<p>Here is <span>1</span> example.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: components/markdown/tests/summary.rs
expression: body
---
<p>Hello world.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: components/markdown/tests/summary.rs
expression: rendered.body
---
<p>Things to do:</p>
<ul>
<li>Program <!-- more --> something</li>
<li>Eat</li>
<li>Sleep</li>
</ul>
39 changes: 35 additions & 4 deletions components/markdown/tests/summary.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod common;

fn get_summary(content: &str) -> String {
let rendered = common::render(content).unwrap();
assert!(rendered.summary_len.is_some());
let summary_len = rendered.summary_len.unwrap();
rendered.body[..summary_len].to_owned()
get_rendered(content).summary.expect("had no summary")
}

fn get_rendered(content: &str) -> markdown::Rendered {
common::render(content).expect("couldn't render")
}

#[test]
Expand Down Expand Up @@ -45,3 +46,33 @@ And some content after
);
insta::assert_snapshot!(body);
}

#[test]
fn no_truncated_summary() {
let rendered = get_rendered(
r#"
Things to do:
* Program <!-- more --> something
* Eat
* Sleep
"#,
);
assert!(rendered.summary.is_none());
insta::assert_snapshot!(rendered.body);
}

#[test]
fn footnotes_summary() {
let body = get_summary(
r#"
Hello world[^1].

<!-- more -->

Good bye.

[^1]: "World" is a placeholder.
"#,
);
insta::assert_snapshot!(body);
}
2 changes: 1 addition & 1 deletion docs/content/documentation/content/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ template = "page.html"
You can ask Zola to create a summary if, for example, you only want to show the first
paragraph of the page content in a list.

To do so, add <code>&lt;!-- more --&gt;</code> in your content at the point
To do so, add `<!-- more -->` in your content at the point
where you want the summary to end. The content up to that point will be
available separately in the
[template](@/documentation/templates/pages-sections.md#page-variables) via `page.summary`.
Expand Down