summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan-Erik Rediger <janerik@fnordig.de>2022-01-25 21:51:56 +0100
committerJan-Erik Rediger <janerik@fnordig.de>2022-01-25 22:17:47 +0100
commitdb5c14c79232c2d6cc5ada8a0fd44c55e448aec2 (patch)
tree7476f95d9763ed6aabf754b0b50529ea984f064e
parent87b54eeeefac6dda1a0fb1fc166cec70339eac7b (diff)
Avoid roundtripping through pulldown-cmark
Roundtripping markdown is actually quite hard. We don't actually require that. All we need is once parsing the markdown to find the mermaid code blocks. We then manually generate HTML and all other content can be copied unparsed again.
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/mdbook-mermaid.rs2
-rw-r--r--src/lib.rs97
4 files changed, 53 insertions, 57 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 043ecb4..3edef42 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -894,7 +894,6 @@ dependencies = [
"mdbook",
"pretty_assertions",
"pulldown-cmark",
- "pulldown-cmark-to-cmark",
"serde_json",
"tempfile",
"toml_edit",
@@ -1308,15 +1307,6 @@ dependencies = [
]
[[package]]
-name = "pulldown-cmark-to-cmark"
-version = "9.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f94dc756ef5c50ad28ccea8428ba5de9f4dca1fff6516a26b85e0b125a70d17"
-dependencies = [
- "pulldown-cmark",
-]
-
-[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 22bbeea..8329a0e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,6 @@ log = "0.4.11"
clap = "2.33.3"
serde_json = "1.0.57"
toml_edit = "0.13.0"
-pulldown-cmark-to-cmark = "9.0.0"
[dev-dependencies]
assert_cmd = "2.0.0"
diff --git a/src/bin/mdbook-mermaid.rs b/src/bin/mdbook-mermaid.rs
index 085bfbb..dec9657 100644
--- a/src/bin/mdbook-mermaid.rs
+++ b/src/bin/mdbook-mermaid.rs
@@ -73,7 +73,7 @@ fn handle_preprocessing() -> Result<(), Error> {
fn handle_supports(sub_args: &ArgMatches) -> ! {
let renderer = sub_args.value_of("renderer").expect("Required argument");
- let supported = Mermaid.supports_renderer(&renderer);
+ let supported = Mermaid.supports_renderer(renderer);
// Signal whether the renderer is supported by exiting with 1 or 0.
if supported {
diff --git a/src/lib.rs b/src/lib.rs
index c128a11..dff9c4e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,7 @@
use mdbook::book::{Book, BookItem, Chapter};
-use mdbook::errors::{Error, Result};
+use mdbook::errors::Result;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use pulldown_cmark::{CodeBlockKind::*, Event, Options, Parser, Tag};
-use pulldown_cmark_to_cmark::{cmark_with_options, Options as COptions};
pub struct Mermaid;
@@ -48,7 +47,6 @@ fn escape_html(s: &str) -> String {
}
fn add_mermaid(content: &str) -> Result<String> {
- let mut buf = String::with_capacity(content.len());
let mut mermaid_content = String::new();
let mut in_mermaid_block = false;
@@ -58,49 +56,49 @@ fn add_mermaid(content: &str) -> Result<String> {
opts.insert(Options::ENABLE_STRIKETHROUGH);
opts.insert(Options::ENABLE_TASKLISTS);
- let events = Parser::new_ext(content, opts).map(|e| {
+ let mut mermaid_start = 0..0;
+
+ let mut mermaid_blocks = vec![];
+
+ let events = Parser::new_ext(content, opts);
+ for (e, span) in events.into_offset_iter() {
if let Event::Start(Tag::CodeBlock(Fenced(code))) = e.clone() {
+ log::debug!("e={:?}, span={:?}", e, span);
if &*code == "mermaid" {
+ mermaid_start = span;
in_mermaid_block = true;
mermaid_content.clear();
- return None;
- } else {
- return Some(e);
}
+ continue;
}
if !in_mermaid_block {
- return Some(e);
+ continue;
}
- match e {
- Event::End(Tag::CodeBlock(Fenced(code))) => {
- assert_eq!(
- "mermaid", &*code,
- "After an opening mermaid code block we expect it to close again"
- );
- in_mermaid_block = false;
-
- let mermaid_content = escape_html(&mermaid_content);
- let mermaid_code = format!("<pre class=\"mermaid\">{}</pre>\n\n", mermaid_content);
- return Some(Event::Html(mermaid_code.into()));
- }
- Event::Text(code) => {
- mermaid_content.push_str(&code);
- }
- _ => return Some(e),
+ if let Event::End(Tag::CodeBlock(Fenced(code))) = e {
+ assert_eq!(
+ "mermaid", &*code,
+ "After an opening mermaid code block we expect it to close again"
+ );
+ in_mermaid_block = false;
+ let pre = "```mermaid\n";
+ let post = "```";
+
+ let mermaid_content = &content[mermaid_start.start + pre.len()..span.end - post.len()];
+ let mermaid_content = escape_html(mermaid_content);
+ let mermaid_code = format!("<pre class=\"mermaid\">{}</pre>\n\n", mermaid_content);
+ mermaid_blocks.push((mermaid_start.start..span.end, mermaid_code.clone()));
}
+ }
- None
- });
- let events = events.flatten();
- let opts = COptions {
- newlines_after_codeblock: 1,
- ..Default::default()
- };
- cmark_with_options(events, &mut buf, None, opts)
- .map(|_| buf)
- .map_err(|err| Error::msg(format!("Markdown serialization failed: {}", err)))
+ let mut content = content.to_string();
+ for (span, block) in mermaid_blocks.iter().rev() {
+ let pre_content = &content[0..span.start];
+ let post_content = &content[span.end..];
+ content = format!("{}\n{}{}", pre_content, block, post_content);
+ }
+ Ok(content)
}
impl Mermaid {
@@ -129,12 +127,15 @@ Text
let expected = r#"# Chapter
+
<pre class="mermaid">graph TD
A --&gt; B
</pre>
-Text"#;
+
+Text
+"#;
assert_eq!(expected, add_mermaid(content).unwrap());
}
@@ -151,12 +152,12 @@ Text"#;
| Row 1 | Row 2 |
"#;
- // Markdown roundtripping removes some insignificant whitespace
let expected = r#"# Heading
-|Head 1|Head 2|
-|------|------|
-|Row 1|Row 2|"#;
+| Head 1 | Head 2 |
+|--------|--------|
+| Row 1 | Row 2 |
+"#;
assert_eq!(expected, add_mermaid(content).unwrap());
}
@@ -175,7 +176,6 @@ Text"#;
</del>
"#;
- // Markdown roundtripping removes some insignificant whitespace
let expected = r#"# Heading
<del>
@@ -202,20 +202,21 @@ Text"#;
2. paragraph 2
"#;
- // Markdown roundtripping removes some insignificant whitespace
let expected = r#"# Heading
1. paragraph 1
- ````
+ ```
code 1
- ````
-1. paragraph 2"#;
+ ```
+2. paragraph 2
+"#;
assert_eq!(expected, add_mermaid(content).unwrap());
}
#[test]
fn escape_in_mermaid_block() {
+ env_logger::init();
let content = r#"
```mermaid
classDiagram
@@ -225,15 +226,21 @@ classDiagram
}
```
+hello
"#;
- let expected = r#"<pre class="mermaid">classDiagram
+ let expected = r#"
+
+<pre class="mermaid">classDiagram
class PingUploader {
&lt;&lt;interface&gt;&gt;
+Upload() UploadResult
}
</pre>
+
+
+hello
"#;
assert_eq!(expected, add_mermaid(content).unwrap());