Compare commits

..

3 Commits

Author SHA1 Message Date
Tom Milligan
b1707e8ba6 wip on pandoc parser 2025-10-22 13:41:08 +01:00
Tom Milligan
5714548254 setup code feature flags 2025-10-22 12:09:06 +01:00
Tom Milligan
11aed56c23 add pandoc crates and features 2025-10-22 12:05:36 +01:00
5 changed files with 170 additions and 5 deletions

37
Cargo.lock generated
View File

@@ -463,6 +463,12 @@ dependencies = [
"dtoa",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "elasticlunr-rs"
version = "3.0.2"
@@ -1039,6 +1045,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
@@ -1226,6 +1241,8 @@ dependencies = [
"log",
"mdbook",
"once_cell",
"pandoc",
"pandoc_ast",
"path-slash",
"pretty_assertions",
"pulldown-cmark 0.13.0",
@@ -1388,6 +1405,26 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "pandoc"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463d53d1a77a4291203dbf9d461365609e6857c95bd7d807098bffdc0a02a65c"
dependencies = [
"itertools",
]
[[package]]
name = "pandoc_ast"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1ff7a92f9fb2cac7cb4b8b697efaf3a30abcb64003d4cd34a67cc1ae35194ce"
dependencies = [
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "parking_lot"
version = "0.12.4"

View File

@@ -35,8 +35,10 @@ env_logger = { version = "0.11", default-features = false, optional = true }
log = "0.4.21"
mdbook = "0.4.40"
once_cell = "1.19.0"
pandoc = { version = "0.8", optional = true }
pandoc_ast = { version = "0.8", optional = true }
path-slash = "0.2.1"
pulldown-cmark = "0.13"
pulldown-cmark = { version = "0.13", optional = true }
regex = "1.10.5"
semver = "1.0.23"
serde = { version = "1.0.203", features = ["derive"] }
@@ -49,7 +51,9 @@ hex_color = { version = "3.0.0", features = ["serde"] }
pretty_assertions = "1.4.0"
[features]
default = ["cli", "cli-install"]
default = ["cli", "cli-install", "parser-markdown"]
parser-markdown = ["pulldown-cmark"]
parser-pandoc = ["pandoc", "pandoc_ast"]
# Enable the command line binary
cli = ["clap", "env_logger"]

View File

@@ -6,7 +6,10 @@ mod book_config;
mod config;
#[doc(hidden)]
pub mod custom;
#[cfg(feature = "parser-markdown")]
mod markdown;
#[cfg(feature = "parser-pandoc")]
mod pandoc;
mod parse;
mod preprocessor;
mod render;

78
src/pandoc.rs Normal file
View File

@@ -0,0 +1,78 @@
use mdbook::errors::Result as MdbookResult;
use crate::{
book_config::OnFailure,
types::{Overrides, RenderTextMode},
};
pub(crate) fn preprocess(
content: &str,
on_failure: OnFailure,
overrides: &Overrides,
render_text_mode: RenderTextMode,
) -> MdbookResult<String> {
if render_text_mode == RenderTextMode::Strip {
// We don't support stripping in pandoc - they just show up as divs.
return Ok(content.to_owned());
}
let ADMONISH_KIND = "note".to_owned();
let mut pandoc = pandoc::new();
pandoc.set_input(pandoc::InputKind::Pipe(content.into()));
pandoc.set_input_format(pandoc::InputFormat::CommonmarkX, Vec::new());
pandoc.set_output_format(pandoc::OutputFormat::CommonmarkX, Vec::new());
pandoc.set_output(pandoc::OutputKind::Pipe);
pandoc.add_filter(|json| {
pandoc_ast::filter(json, |mut pandoc| {
for block in &mut pandoc.blocks {
println!("{:?}", &block);
use pandoc_ast::Block;
use pandoc_ast::Inline;
match block {
Block::Div((identifier, classes, kv), content) => {
if let Some(ADMONISH_KIND) = classes.first() {
// TODO: generate values for below by reading the pandoc input
// decide on a syntax for kv and classes, admonition type, and plug
// that in to our existing structs which deal with links and overrides
// etc.
*block = Block::Div(
(
// TODO: get from kv
"admonition-title".to_owned(),
// TODO: compute from input above
vec!["admonition".to_owned(), "admonish-note".to_owned()],
vec![
("role".to_owned(), "note".to_owned()),
(
"aria-labelledby".to_owned(),
"admonition-title-title".to_owned(),
),
],
),
vec![
Block::Div(
// TODO: add anchor link elements (a, href, etc)
("admonition-title-title".to_owned(), vec![], vec![]),
vec![Block::Plain(vec![Inline::Str("Title".to_owned())])],
),
Block::Div(("".to_owned(), vec![], vec![]), content.clone()),
],
)
}
}
_ => {}
}
}
pandoc
})
});
let result = pandoc.execute().unwrap();
use pandoc::PandocOutput::*;
let output = match result {
ToBuffer(output) => output,
ToFile(_) | ToBufferRaw(_) => panic!("expected buffer output"),
};
Ok(output)
}

View File

@@ -7,10 +7,14 @@ use mdbook::{
use crate::{
book_config::{admonish_config_from_context, Config, RenderMode},
markdown::preprocess,
types::{Overrides, RenderTextMode},
};
#[cfg(feature = "parser-markdown")]
use crate::markdown::preprocess;
#[cfg(feature = "parser-pandoc")]
use crate::pandoc::preprocess;
pub struct Admonish;
impl Preprocessor for Admonish {
@@ -166,8 +170,9 @@ mod test {
serde_json::from_value(value).unwrap()
}
#[cfg(feature = "parser-markdown")]
#[test]
fn run_html() {
fn run_markdown_to_html() {
let content = r#"
````admonish title="Title"
```rust
@@ -210,6 +215,43 @@ x = 20;
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
#[cfg(feature = "parser-pandoc")]
#[test]
fn run_pandoc_to_html() {
let content = r#"
:::{.note title="Title"}
```rust
let x = 10;
x = 20;
```
:::
"#;
let expected_content = r##"::::: {#admonition-title .admonition .admonish-note role="note" aria-labelledby="admonition-title-title"}
::: {#admonition-title-title}
Title
:::
::: {}
``` rust
let x = 10;
x = 20;
```
:::
:::::
"##;
let ctx = mock_context(
&json!({
"assets_version": "3.0.0"
}),
"html",
);
let book = mock_book(content);
let expected_book = mock_book(expected_content);
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
#[test]
fn run_test_preserves_by_default() {
let content = r#"
@@ -232,8 +274,9 @@ x = 20;
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
#[cfg(feature = "parser-markdown")]
#[test]
fn run_test_can_strip() {
fn run_test_can_strip_markdown() {
let content = r#"
````admonish title="Title"
```rust