Update pulldown_cmark
This commit is contained in:
parent
40d7208493
commit
a67370b8d8
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
## 0.16.0 (unreleased)
|
## 0.16.0 (unreleased)
|
||||||
|
|
||||||
|
### Breaking
|
||||||
|
|
||||||
|
- Switch to pulldown-cmark anchor rather than ours, some (very niche) edge cases are not supported anymore, you can
|
||||||
|
also specify classes on headers now
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- Fix markup for fenced code with linenos
|
||||||
|
- Make `ignored_content` work with nested paths and directories
|
||||||
|
- `zola serve/build` can now run from anywhere in a zola directory
|
||||||
|
|
||||||
## 0.15.3 (2022-01-23)
|
## 0.15.3 (2022-01-23)
|
||||||
|
|
||||||
|
|
|
@ -2372,9 +2372,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
version = "0.8.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
|
@ -25,7 +25,7 @@ svg_metadata = "0.4"
|
||||||
slotmap = "1"
|
slotmap = "1"
|
||||||
lexical-sort = "0.3"
|
lexical-sort = "0.3"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
pulldown-cmark = { version = "0.8", default-features = false }
|
pulldown-cmark = { version = "0.9", default-features = false, features = ["simd"] }
|
||||||
gh-emoji = "1"
|
gh-emoji = "1"
|
||||||
elasticlunr-rs = {version = "2", default-features = false, features = ["da", "no", "de", "du", "es", "fi", "fr", "it", "pt", "ro", "ru", "sv", "tr"] }
|
elasticlunr-rs = {version = "2", default-features = false, features = ["da", "no", "de", "du", "es", "fi", "fr", "it", "pt", "ro", "ru", "sv", "tr"] }
|
||||||
ammonia = "3"
|
ammonia = "3"
|
||||||
|
|
|
@ -2,11 +2,13 @@ use libs::gh_emoji::Replacer as EmojiReplacer;
|
||||||
use libs::once_cell::sync::Lazy;
|
use libs::once_cell::sync::Lazy;
|
||||||
use libs::pulldown_cmark as cmark;
|
use libs::pulldown_cmark as cmark;
|
||||||
use libs::tera;
|
use libs::tera;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use crate::context::RenderContext;
|
use crate::context::RenderContext;
|
||||||
use crate::table_of_contents::{make_table_of_contents, Heading};
|
use crate::table_of_contents::{make_table_of_contents, Heading};
|
||||||
use errors::{Error, Result};
|
use errors::{Error, Result};
|
||||||
use front_matter::InsertAnchor;
|
use front_matter::InsertAnchor;
|
||||||
|
use libs::pulldown_cmark::escape::escape_html;
|
||||||
use utils::site::resolve_internal_link;
|
use utils::site::resolve_internal_link;
|
||||||
use utils::slugs::slugify_anchors;
|
use utils::slugs::slugify_anchors;
|
||||||
use utils::vec::InsertMany;
|
use utils::vec::InsertMany;
|
||||||
|
@ -37,11 +39,39 @@ struct HeadingRef {
|
||||||
end_idx: usize,
|
end_idx: usize,
|
||||||
level: u32,
|
level: u32,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
|
classes: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadingRef {
|
impl HeadingRef {
|
||||||
fn new(start: usize, level: u32) -> HeadingRef {
|
fn new(start: usize, level: u32, anchor: Option<String>, classes: &[String]) -> HeadingRef {
|
||||||
HeadingRef { start_idx: start, end_idx: 0, level, id: None }
|
HeadingRef { start_idx: start, end_idx: 0, level, id: anchor, classes: classes.to_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_html(&self, id: &str) -> String {
|
||||||
|
let mut buffer = String::with_capacity(100);
|
||||||
|
buffer.write_str("<h").unwrap();
|
||||||
|
buffer.write_str(&format!("{}", self.level)).unwrap();
|
||||||
|
|
||||||
|
buffer.write_str(" id=\"").unwrap();
|
||||||
|
escape_html(&mut buffer, id).unwrap();
|
||||||
|
buffer.write_str("\"").unwrap();
|
||||||
|
|
||||||
|
if !self.classes.is_empty() {
|
||||||
|
buffer.write_str(" class=\"").unwrap();
|
||||||
|
let num_classes = self.classes.len();
|
||||||
|
|
||||||
|
for (i, class) in self.classes.iter().enumerate() {
|
||||||
|
escape_html(&mut buffer, class).unwrap();
|
||||||
|
if i < num_classes - 1 {
|
||||||
|
buffer.write_str(" ").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.write_str("\"").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.write_str(">").unwrap();
|
||||||
|
buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,10 +161,15 @@ fn get_heading_refs(events: &[Event]) -> Vec<HeadingRef> {
|
||||||
|
|
||||||
for (i, event) in events.iter().enumerate() {
|
for (i, event) in events.iter().enumerate() {
|
||||||
match event {
|
match event {
|
||||||
Event::Start(Tag::Heading(level)) => {
|
Event::Start(Tag::Heading(level, anchor, classes)) => {
|
||||||
heading_refs.push(HeadingRef::new(i, *level));
|
heading_refs.push(HeadingRef::new(
|
||||||
|
i,
|
||||||
|
*level as u32,
|
||||||
|
anchor.map(|a| a.to_owned()),
|
||||||
|
&classes.iter().map(|x| x.to_string()).collect::<Vec<_>>(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Event::End(Tag::Heading(_)) => {
|
Event::End(Tag::Heading(_, _, _)) => {
|
||||||
heading_refs.last_mut().expect("Heading end before start?").end_idx = i;
|
heading_refs.last_mut().expect("Heading end before start?").end_idx = i;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -161,7 +196,6 @@ pub fn markdown_to_html(
|
||||||
|
|
||||||
let mut code_block: Option<CodeBlock> = None;
|
let mut code_block: Option<CodeBlock> = None;
|
||||||
|
|
||||||
let mut inserted_anchors: Vec<String> = vec![];
|
|
||||||
let mut headings: Vec<Heading> = vec![];
|
let mut headings: Vec<Heading> = vec![];
|
||||||
let mut internal_links = Vec::new();
|
let mut internal_links = Vec::new();
|
||||||
let mut external_links = Vec::new();
|
let mut external_links = Vec::new();
|
||||||
|
@ -174,6 +208,7 @@ pub fn markdown_to_html(
|
||||||
opts.insert(Options::ENABLE_FOOTNOTES);
|
opts.insert(Options::ENABLE_FOOTNOTES);
|
||||||
opts.insert(Options::ENABLE_STRIKETHROUGH);
|
opts.insert(Options::ENABLE_STRIKETHROUGH);
|
||||||
opts.insert(Options::ENABLE_TASKLISTS);
|
opts.insert(Options::ENABLE_TASKLISTS);
|
||||||
|
opts.insert(Options::ENABLE_HEADING_ATTRIBUTES);
|
||||||
|
|
||||||
if context.config.markdown.smart_punctuation {
|
if context.config.markdown.smart_punctuation {
|
||||||
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
|
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||||
|
@ -389,45 +424,34 @@ pub fn markdown_to_html(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut heading_refs = get_heading_refs(&events);
|
let heading_refs = get_heading_refs(&events);
|
||||||
|
|
||||||
let mut anchors_to_insert = vec![];
|
let mut anchors_to_insert = vec![];
|
||||||
|
let mut inserted_anchors = vec![];
|
||||||
// First heading pass: look for a manually-specified IDs, e.g. `# Heading text {#hash}`
|
for heading in &heading_refs {
|
||||||
// (This is a separate first pass so that auto IDs can avoid collisions with manual IDs.)
|
if let Some(s) = &heading.id {
|
||||||
for heading_ref in heading_refs.iter_mut() {
|
inserted_anchors.push(s.to_owned());
|
||||||
let end_idx = heading_ref.end_idx;
|
|
||||||
if let Event::Text(ref mut text) = events[end_idx - 1] {
|
|
||||||
if text.as_bytes().last() == Some(&b'}') {
|
|
||||||
if let Some(mut i) = text.find("{#") {
|
|
||||||
let id = text[i + 2..text.len() - 1].to_owned();
|
|
||||||
inserted_anchors.push(id.clone());
|
|
||||||
while i > 0 && text.as_bytes()[i - 1] == b' ' {
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
heading_ref.id = Some(id);
|
|
||||||
*text = text[..i].to_owned().into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second heading pass: auto-generate remaining IDs, and emit HTML
|
// Second heading pass: auto-generate remaining IDs, and emit HTML
|
||||||
for heading_ref in heading_refs {
|
for mut heading_ref in heading_refs {
|
||||||
let start_idx = heading_ref.start_idx;
|
let start_idx = heading_ref.start_idx;
|
||||||
let end_idx = heading_ref.end_idx;
|
let end_idx = heading_ref.end_idx;
|
||||||
let title = get_text(&events[start_idx + 1..end_idx]);
|
let title = get_text(&events[start_idx + 1..end_idx]);
|
||||||
let id = heading_ref.id.unwrap_or_else(|| {
|
|
||||||
find_anchor(
|
if heading_ref.id.is_none() {
|
||||||
|
heading_ref.id = Some(find_anchor(
|
||||||
&inserted_anchors,
|
&inserted_anchors,
|
||||||
slugify_anchors(&title, context.config.slugify.anchors),
|
slugify_anchors(&title, context.config.slugify.anchors),
|
||||||
0,
|
0,
|
||||||
)
|
));
|
||||||
});
|
}
|
||||||
inserted_anchors.push(id.clone());
|
|
||||||
|
|
||||||
// insert `id` to the tag
|
inserted_anchors.push(heading_ref.id.clone().unwrap());
|
||||||
let html = format!("<h{lvl} id=\"{id}\">", lvl = heading_ref.level, id = id);
|
let id = inserted_anchors.last().unwrap();
|
||||||
|
|
||||||
|
let html = heading_ref.to_html(&id);
|
||||||
events[start_idx] = Event::Html(html.into());
|
events[start_idx] = Event::Html(html.into());
|
||||||
|
|
||||||
// generate anchors and places to insert them
|
// generate anchors and places to insert them
|
||||||
|
@ -454,8 +478,13 @@ pub fn markdown_to_html(
|
||||||
|
|
||||||
// record heading to make table of contents
|
// record heading to make table of contents
|
||||||
let permalink = format!("{}#{}", context.current_page_permalink, id);
|
let permalink = format!("{}#{}", context.current_page_permalink, id);
|
||||||
let h =
|
let h = Heading {
|
||||||
Heading { level: heading_ref.level, id, permalink, title, children: Vec::new() };
|
level: heading_ref.level,
|
||||||
|
id: id.to_owned(),
|
||||||
|
permalink,
|
||||||
|
title,
|
||||||
|
children: Vec::new(),
|
||||||
|
};
|
||||||
headings.push(h);
|
headings.push(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use libs::tera::Tera;
|
use libs::tera::Tera;
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use errors::Result;
|
|
||||||
use front_matter::InsertAnchor;
|
use front_matter::InsertAnchor;
|
||||||
use rendering::{render_content, RenderContext, Rendered};
|
use rendering::{render_content, RenderContext};
|
||||||
use templates::ZOLA_TERA;
|
use templates::ZOLA_TERA;
|
||||||
use utils::slugs::SlugifyStrategy;
|
use utils::slugs::SlugifyStrategy;
|
||||||
|
|
||||||
|
@ -47,11 +45,6 @@ fn can_make_zola_internal_links() {
|
||||||
fn can_handle_heading_ids() {
|
fn can_handle_heading_ids() {
|
||||||
let mut config = Config::default_for_test();
|
let mut config = Config::default_for_test();
|
||||||
|
|
||||||
// Tested things: manual IDs; whitespace flexibility; that automatic IDs avoid collision with
|
|
||||||
// manual IDs; that duplicates are in fact permitted among manual IDs; that any non-plain-text
|
|
||||||
// in the middle of `{#…}` will disrupt it from being acknowledged as a manual ID (that last
|
|
||||||
// one could reasonably be considered a bug rather than a feature, but test it either way); one
|
|
||||||
// workaround for the improbable case where you actually want `{#…}` at the end of a heading.
|
|
||||||
let cases = vec![
|
let cases = vec![
|
||||||
// Basic
|
// Basic
|
||||||
"# Hello",
|
"# Hello",
|
||||||
|
@ -79,6 +72,8 @@ fn can_handle_heading_ids() {
|
||||||
"# **hi**",
|
"# **hi**",
|
||||||
// See https://github.com/getzola/zola/issues/569
|
// See https://github.com/getzola/zola/issues/569
|
||||||
"# text [^1] there\n[^1]: footnote",
|
"# text [^1] there\n[^1]: footnote",
|
||||||
|
// Chosen slug that already exists with space
|
||||||
|
"# Classes {#classes .bold .another}",
|
||||||
];
|
];
|
||||||
let body = common::render_with_config(&cases.join("\n"), config.clone()).unwrap().body;
|
let body = common::render_with_config(&cases.join("\n"), config.clone()).unwrap().body;
|
||||||
insta::assert_snapshot!(body);
|
insta::assert_snapshot!(body);
|
||||||
|
|
|
@ -101,10 +101,10 @@ console.log(foo(5));
|
||||||
<tr><td>ext</td><td>extension to be used for dest files.</td></tr>
|
<tr><td>ext</td><td>extension to be used for dest files.</td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
<p>Right aligned columns</p>
|
<p>Right aligned columns</p>
|
||||||
<table><thead><tr><th align="right">Option</th><th align="right">Description</th></tr></thead><tbody>
|
<table><thead><tr><th style="text-align: right">Option</th><th style="text-align: right">Description</th></tr></thead><tbody>
|
||||||
<tr><td align="right">data</td><td align="right">path to data files to supply the data that will be passed into templates.</td></tr>
|
<tr><td style="text-align: right">data</td><td style="text-align: right">path to data files to supply the data that will be passed into templates.</td></tr>
|
||||||
<tr><td align="right">engine</td><td align="right">engine to be used for processing templates. Handlebars is the default.</td></tr>
|
<tr><td style="text-align: right">engine</td><td style="text-align: right">engine to be used for processing templates. Handlebars is the default.</td></tr>
|
||||||
<tr><td align="right">ext</td><td align="right">extension to be used for dest files.</td></tr>
|
<tr><td style="text-align: right">ext</td><td style="text-align: right">extension to be used for dest files.</td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
<h2 id="links">Links</h2>
|
<h2 id="links">Links</h2>
|
||||||
<p><a href="http://duckduckgo.com">link text</a></p>
|
<p><a href="http://duckduckgo.com">link text</a></p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: components/rendering/tests/markdown.rs
|
source: components/rendering/tests/markdown.rs
|
||||||
assertion_line: 89
|
assertion_line: 84
|
||||||
expression: body
|
expression: body
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -11,7 +11,7 @@ expression: body
|
||||||
<h1 id="hello">Hello</h1>
|
<h1 id="hello">Hello</h1>
|
||||||
<h1 id="Something_else">Hello</h1>
|
<h1 id="Something_else">Hello</h1>
|
||||||
<h1 id="Workaround_for_literal_{#…}">Workaround for literal {#…}</h1>
|
<h1 id="Workaround_for_literal_{#…}">Workaround for literal {#…}</h1>
|
||||||
<h1 id="Auto_{#matic}">Auto {#<em>matic</em>}</h1>
|
<h1 id="*matic*">Auto</h1>
|
||||||
<h1 id=""></h1>
|
<h1 id=""></h1>
|
||||||
<h1 id="-1"></h1>
|
<h1 id="-1"></h1>
|
||||||
<h1 id="About"><a href="https://getzola.org/about/">About</a></h1>
|
<h1 id="About"><a href="https://getzola.org/about/">About</a></h1>
|
||||||
|
@ -22,5 +22,6 @@ expression: body
|
||||||
<h1 id="text__there">text <sup class="footnote-reference"><a href="#1">1</a></sup> there</h1>
|
<h1 id="text__there">text <sup class="footnote-reference"><a href="#1">1</a></sup> there</h1>
|
||||||
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
||||||
<p>footnote</p>
|
<p>footnote</p>
|
||||||
|
<h1 id="classes" class="bold another">Classes</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
source: components/rendering/tests/markdown.rs
|
source: components/rendering/tests/markdown.rs
|
||||||
assertion_line: 84
|
assertion_line: 79
|
||||||
expression: body
|
expression: body
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -11,7 +11,7 @@ expression: body
|
||||||
<h1 id="hello">Hello</h1>
|
<h1 id="hello">Hello</h1>
|
||||||
<h1 id="Something_else">Hello</h1>
|
<h1 id="Something_else">Hello</h1>
|
||||||
<h1 id="workaround-for-literal">Workaround for literal {#…}</h1>
|
<h1 id="workaround-for-literal">Workaround for literal {#…}</h1>
|
||||||
<h1 id="auto-matic">Auto {#<em>matic</em>}</h1>
|
<h1 id="*matic*">Auto</h1>
|
||||||
<h1 id=""></h1>
|
<h1 id=""></h1>
|
||||||
<h1 id="-1"></h1>
|
<h1 id="-1"></h1>
|
||||||
<h1 id="about"><a href="https://getzola.org/about/">About</a></h1>
|
<h1 id="about"><a href="https://getzola.org/about/">About</a></h1>
|
||||||
|
@ -22,5 +22,6 @@ expression: body
|
||||||
<h1 id="text-there">text <sup class="footnote-reference"><a href="#1">1</a></sup> there</h1>
|
<h1 id="text-there">text <sup class="footnote-reference"><a href="#1">1</a></sup> there</h1>
|
||||||
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
||||||
<p>footnote</p>
|
<p>footnote</p>
|
||||||
|
<h1 id="classes" class="bold another">Classes</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,10 @@ For example:
|
||||||
## Example code <- example-code-1
|
## Example code <- example-code-1
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also manually specify an id with a `{#…}` suffix on the heading line:
|
You can also manually specify an id with a `{#…}` suffix on the heading line as well as CSS classes:
|
||||||
|
|
||||||
```md
|
```md
|
||||||
# Something manual! {#manual}
|
# Something manual! {#manual .header .bold}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is useful for making deep links robust, either proactively (so that you can later change the text of a heading
|
This is useful for making deep links robust, either proactively (so that you can later change the text of a heading
|
||||||
|
|
Loading…
Reference in New Issue