Skip to content

Commit

Permalink
feat: support block quote tags e.g. [!NOTE]
Browse files Browse the repository at this point in the history
  • Loading branch information
max-heller committed Jan 23, 2025
1 parent f84e3af commit 4e7f4dc
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 22 deletions.
15 changes: 13 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ log = "0.4.0"
mdbook = { version = "0.4.35", default-features = false }
normpath = "1.0.0"
once_cell = "1.0.0"
pulldown-cmark = { version = "0.10.0", default-features = false }
pulldown-cmark = { version = "0.12.2", default-features = false }
regex = "1.5.5"
ego-tree = "0.10.0"
replace_with = "0.1.7"
Expand Down
9 changes: 8 additions & 1 deletion src/preprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ impl<'book> Parser<'book> {
.union(Options::ENABLE_TABLES)
.union(Options::ENABLE_TASKLISTS)
.union(Options::ENABLE_HEADING_ATTRIBUTES)
.union(Options::ENABLE_GFM)
};

Self {
Expand Down Expand Up @@ -952,7 +953,7 @@ impl<'book, 'preprocessor> PreprocessChapter<'book, 'preprocessor> {
push_element(self, tree, MdElement::Link { dest_url, title })
}
Tag::Paragraph => push_element(self, tree, MdElement::Paragraph),
Tag::BlockQuote => push_element(self, tree, MdElement::BlockQuote),
Tag::BlockQuote(kind) => push_element(self, tree, MdElement::BlockQuote(kind)),
Tag::CodeBlock(kind) => push_element(self, tree, MdElement::CodeBlock(kind)),
Tag::Emphasis => push_element(self, tree, MdElement::Emphasis),
Tag::Strong => push_element(self, tree, MdElement::Strong),
Expand Down Expand Up @@ -982,6 +983,10 @@ impl<'book, 'preprocessor> PreprocessChapter<'book, 'preprocessor> {
}
return Ok(());
}
// Definition list parsing is not enabled
Tag::DefinitionList
| Tag::DefinitionListTitle
| Tag::DefinitionListDefinition => unreachable!(),
}?;
Ok(())
}
Expand Down Expand Up @@ -1049,6 +1054,8 @@ impl<'book, 'preprocessor> PreprocessChapter<'book, 'preprocessor> {
tree.create_element(MdElement::TaskListMarker(checked))?;
Ok(())
}
// Math option is not enabled
Event::InlineMath(_) | Event::DisplayMath(_) => unreachable!(),
}
}

Expand Down
34 changes: 28 additions & 6 deletions src/preprocess/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use html5ever::{
LocalName,
};
use indexmap::IndexSet;
use pulldown_cmark::{CowStr, LinkType};
use pulldown_cmark::{BlockQuoteKind, CowStr, LinkType};

use crate::{html, latex, pandoc, preprocess::UnresolvableRemoteImage};

Expand Down Expand Up @@ -378,15 +378,37 @@ impl<'book> Emitter<'book> {
}
}
},
MdElement::BlockQuote => serializer
MdElement::BlockQuote(Some(kind)) => {
let (class, title) = match kind {
BlockQuoteKind::Note => ("note", "Note"),
BlockQuoteKind::Tip => ("tip", "Tip"),
BlockQuoteKind::Important => ("important", "Important"),
BlockQuoteKind::Warning => ("warning", "Warning"),
BlockQuoteKind::Caution => ("caution", "Caution"),
};
serializer.blocks()?.serialize_element()?.serialize_div(
(None, &[class.into()], &[]),
|blocks| {
blocks.serialize_element()?.serialize_div(
(None, &["title".into()], &[]),
|header| {
header.serialize_element()?.serialize_para(|header| {
header.serialize_element()?.serialize_str(title)
})
},
)?;
blocks.serialize_nested(|serializer| {
self.serialize_children(node, serializer)
})
},
)
}
MdElement::BlockQuote(None) => serializer
.blocks()?
.serialize_element()?
.serialize_block_quote(|blocks| {
blocks.serialize_nested(|serializer| {
for node in node.children() {
self.serialize_node(node, serializer)?;
}
Ok(())
self.serialize_children(node, serializer)
})
}),
MdElement::InlineCode(s) => serializer.serialize_inlines(|inlines| {
Expand Down
6 changes: 3 additions & 3 deletions src/preprocess/tree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fmt;

use html5ever::{local_name, namespace_url, ns, tendril::StrTendril, Attribute, QualName};
use indexmap::IndexMap;
use pulldown_cmark::{Alignment, CodeBlockKind, CowStr, HeadingLevel, LinkType};
use pulldown_cmark::{Alignment, BlockQuoteKind, CodeBlockKind, CowStr, HeadingLevel, LinkType};

use crate::html;

Expand Down Expand Up @@ -52,7 +52,7 @@ pub enum MdElement<'a> {
classes: Vec<CowStr<'a>>,
attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
},
BlockQuote,
BlockQuote(Option<BlockQuoteKind>),
InlineCode(CowStr<'a>),
CodeBlock(CodeBlockKind<'a>),
List(Option<u64>),
Expand Down Expand Up @@ -218,7 +218,7 @@ impl MdElement<'_> {
HeadingLevel::H6 => H6,
}
}
MdElement::BlockQuote => {
MdElement::BlockQuote(_) => {
const BLOCKQUOTE: &QualName = &html::name!(html "blockquote");
BLOCKQUOTE
}
Expand Down
40 changes: 40 additions & 0 deletions src/tests/alerts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use indoc::indoc;

use super::{Chapter, Config, MDBook};

#[test]
fn alerts() {
let alert = indoc! {"
> [!NOTE]
> Highlights information that users should take into account, even when skimming.
"};
let latex = MDBook::init()
.chapter(Chapter::new("", alert, "chapter.md"))
.config(Config::latex())
.build();
insta::assert_snapshot!(latex, @r#"
├─ log output
│ INFO mdbook::book: Running the pandoc backend
│ INFO mdbook_pandoc::pandoc::renderer: Running pandoc
│ INFO mdbook_pandoc::pandoc::renderer: Wrote output to book/latex/output.tex
├─ latex/output.tex
│ Note
│ Highlights information that users should take into account, even when skimming.
├─ latex/src/chapter.md
│ [Div ("", ["note"], []) [Div ("", ["title"], []) [Para [Str "Note"]], Para [Str "Highlights information that users should take into account, even when skimming."]]]
"#);
let markdown = MDBook::init()
.chapter(Chapter::new("", alert, "chapter.md"))
.config(Config::markdown())
.build();
insta::assert_snapshot!(markdown, @r"
├─ log output
│ INFO mdbook::book: Running the pandoc backend
│ INFO mdbook_pandoc::pandoc::renderer: Running pandoc
│ INFO mdbook_pandoc::pandoc::renderer: Wrote output to book/markdown/book.md
├─ markdown/book.md
│ > [!NOTE]
│ > Highlights information that users should take into account, even when skimming.
");
}
22 changes: 13 additions & 9 deletions src/tests/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,30 @@ fn html_comments() {
}

#[test]
fn nested_html_block() {
fn noncontiguous_html() {
// HTML comment is noncontiguous in the source because it is nested in a block quote.
// Parsing should handle this sanely.
let s = indoc! {"
> <!-- hello
>
> world -->
"};
let output = MDBook::init()
.config(Config::markdown())
.config(Config::pandoc())
.chapter(Chapter::new("", s, "chapter.md"))
.build();
insta::assert_snapshot!(output, @r"
insta::assert_snapshot!(output, @r#"
├─ log output
│ INFO mdbook::book: Running the pandoc backend
│ INFO mdbook_pandoc::pandoc::renderer: Running pandoc
│ INFO mdbook_pandoc::pandoc::renderer: Wrote output to book/markdown/book.md
├─ markdown/book.md
│ > <!-- hello
│ >
│ > world -->
");
│ INFO mdbook_pandoc::pandoc::renderer: Wrote output to book/markdown/pandoc-ir
├─ markdown/pandoc-ir
│ [ BlockQuote
│ [ RawBlock (Format "html") "<!-- hello\n\nworld -->"
│ , Plain [ Str "\n" ]
│ ]
│ ]
"#);
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ impl Config {

[profile.markdown]
output-file = "book.md"
to = "commonmark_x"
standalone = false
}
.try_into()
Expand All @@ -389,6 +390,7 @@ mod basic;
mod config;
mod escaping;

mod alerts;
mod code;
mod css;
mod fonts;
Expand Down

0 comments on commit 4e7f4dc

Please sign in to comment.