Skip to content

Commit 1f73c4a

Browse files
committed
Fix shortcode/continue-reading parsing with inline HTML (smaller version)
1 parent 051beca commit 1f73c4a

File tree

8 files changed

+99
-31
lines changed

8 files changed

+99
-31
lines changed

components/content/src/page.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ static RFC3339_DATE: Lazy<Regex> = Lazy::new(|| {
3131
).unwrap()
3232
});
3333

34-
static FOOTNOTES_RE: Lazy<Regex> = Lazy::new(|| {
35-
Regex::new(r#"<sup class="footnote-reference"><a href=\s*.*?>\s*.*?</a></sup>"#).unwrap()
36-
});
37-
3834
#[derive(Clone, Debug, Default, PartialEq, Eq)]
3935
pub struct Page {
4036
/// All info about the actual file
@@ -232,10 +228,7 @@ impl Page {
232228
let res = render_content(&self.raw_content, &context)
233229
.with_context(|| format!("Failed to render content of {}", self.file.path.display()))?;
234230

235-
self.summary = res
236-
.summary_len
237-
.map(|l| &res.body[0..l])
238-
.map(|s| FOOTNOTES_RE.replace_all(s, "").into_owned());
231+
self.summary = res.summary;
239232
self.content = res.body;
240233
self.toc = res.toc;
241234
self.external_links = res.external_links;

components/markdown/src/markdown.rs

+38-18
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ static MORE_DIVIDER_RE: Lazy<Regex> = Lazy::new(|| {
3636
.unwrap()
3737
});
3838

39+
static FOOTNOTES_RE: Lazy<Regex> = Lazy::new(|| {
40+
Regex::new(r#"<sup class="footnote-reference"><a href=\s*.*?>\s*.*?</a></sup>"#).unwrap()
41+
});
42+
3943
/// Although there exists [a list of registered URI schemes][uri-schemes], a link may use arbitrary,
4044
/// private schemes. This regex checks if the given string starts with something that just looks
4145
/// like a scheme, i.e., a case-insensitive identifier followed by a colon.
@@ -78,7 +82,7 @@ fn is_colocated_asset_link(link: &str) -> bool {
7882
#[derive(Debug)]
7983
pub struct Rendered {
8084
pub body: String,
81-
pub summary_len: Option<usize>,
85+
pub summary: Option<String>,
8286
pub toc: Vec<Heading>,
8387
/// Links to site-local pages: relative path plus optional anchor target.
8488
pub internal_links: Vec<(String, Option<String>)>,
@@ -405,6 +409,7 @@ pub fn markdown_to_html(
405409
.map(|x| x.as_object().unwrap().get("relative_path").unwrap().as_str().unwrap());
406410
// the rendered html
407411
let mut html = String::with_capacity(content.len());
412+
let mut summary = None;
408413
// Set while parsing
409414
let mut error = None;
410415

@@ -679,17 +684,15 @@ pub fn markdown_to_html(
679684
event
680685
});
681686
}
682-
Event::Html(text) => {
683-
if !has_summary && MORE_DIVIDER_RE.is_match(&text) {
684-
has_summary = true;
685-
events.push(Event::Html(CONTINUE_READING.into()));
686-
continue;
687-
}
688-
if !contains_shortcode(text.as_ref()) {
689-
events.push(Event::Html(text));
690-
continue;
691-
}
692-
687+
Event::Html(text) | Event::InlineHtml(text)
688+
if !has_summary && MORE_DIVIDER_RE.is_match(text.as_ref()) =>
689+
{
690+
has_summary = true;
691+
events.push(Event::Html(CONTINUE_READING.into()));
692+
}
693+
Event::Html(text) | Event::InlineHtml(text)
694+
if contains_shortcode(text.as_ref()) =>
695+
{
693696
render_shortcodes!(false, text, range);
694697
}
695698
_ => events.push(event),
@@ -781,14 +784,31 @@ pub fn markdown_to_html(
781784
convert_footnotes_to_github_style(&mut events);
782785
}
783786

784-
cmark::html::push_html(&mut html, events.into_iter());
787+
let continue_reading = events
788+
.iter()
789+
.position(|e| matches!(e, Event::Html(CowStr::Borrowed(CONTINUE_READING))))
790+
.unwrap_or(events.len());
791+
792+
let mut events = events.into_iter();
793+
794+
// emit everything up to summary
795+
cmark::html::push_html(&mut html, events.by_ref().take(continue_reading));
796+
797+
if has_summary {
798+
// remove footnotes
799+
let summary_html = FOOTNOTES_RE.replace_all(&html, "").into_owned();
800+
summary = Some(summary_html)
801+
}
802+
803+
// emit everything after summary
804+
cmark::html::push_html(&mut html, events);
785805
}
786806

787807
if let Some(e) = error {
788808
Err(e)
789809
} else {
790810
Ok(Rendered {
791-
summary_len: if has_summary { html.find(CONTINUE_READING) } else { None },
811+
summary,
792812
body: html,
793813
toc: make_table_of_contents(headings),
794814
internal_links,
@@ -861,10 +881,10 @@ mod tests {
861881
for more in mores {
862882
let content = format!("{top}\n\n{more}\n\n{bottom}");
863883
let rendered = markdown_to_html(&content, &context, vec![]).unwrap();
864-
assert!(rendered.summary_len.is_some(), "no summary when splitting on {more}");
865-
let summary_len = rendered.summary_len.unwrap();
866-
let summary = &rendered.body[..summary_len].trim();
867-
let body = &rendered.body[summary_len..].trim();
884+
assert!(rendered.summary.is_some(), "no summary when splitting on {more}");
885+
let summary = rendered.summary.unwrap();
886+
let summary = summary.trim();
887+
let body = rendered.body[summary.len()..].trim();
868888
let continue_reading = &body[..CONTINUE_READING.len()];
869889
let body = &body[CONTINUE_READING.len()..].trim();
870890
assert_eq!(summary, &top_rendered);

components/markdown/tests/shortcodes.rs

+12
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,15 @@ fn can_use_shortcodes_in_quotes() {
311311
.body;
312312
insta::assert_snapshot!(body);
313313
}
314+
315+
#[test]
316+
fn can_render_with_inline_html() {
317+
let body = common::render(
318+
r#"
319+
Here is <span>{{ ex1(page="") }}</span> example.
320+
"#,
321+
)
322+
.unwrap()
323+
.body;
324+
insta::assert_snapshot!(body);
325+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: components/markdown/tests/shortcodes.rs
3+
expression: body
4+
---
5+
<p>Here is <span>1</span> example.</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: components/markdown/tests/summary.rs
3+
expression: body
4+
---
5+
<p>Hello world.</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: components/markdown/tests/summary.rs
3+
expression: body
4+
---
5+
<p>Things to do:</p>
6+
<ul>
7+
<li>Program

components/markdown/tests/summary.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
mod common;
22

33
fn get_summary(content: &str) -> String {
4-
let rendered = common::render(content).unwrap();
5-
assert!(rendered.summary_len.is_some());
6-
let summary_len = rendered.summary_len.unwrap();
7-
rendered.body[..summary_len].to_owned()
4+
common::render(content).expect("couldn't render").summary.expect("had no summary")
85
}
96

107
#[test]
@@ -45,3 +42,32 @@ And some content after
4542
);
4643
insta::assert_snapshot!(body);
4744
}
45+
46+
#[test]
47+
fn truncated_summary() {
48+
let body = get_summary(
49+
r#"
50+
Things to do:
51+
* Program <!-- more --> something
52+
* Eat
53+
* Sleep
54+
"#,
55+
);
56+
insta::assert_snapshot!(body);
57+
}
58+
59+
#[test]
60+
fn footnotes_summary() {
61+
let body = get_summary(
62+
r#"
63+
Hello world[^1].
64+
65+
<!-- more -->
66+
67+
Good bye.
68+
69+
[^1]: "World" is a placeholder.
70+
"#,
71+
);
72+
insta::assert_snapshot!(body);
73+
}

docs/content/documentation/content/page.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ template = "page.html"
155155
You can ask Zola to create a summary if, for example, you only want to show the first
156156
paragraph of the page content in a list.
157157

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

0 commit comments

Comments
 (0)