Repository: rust-lang/mdBook (language: rust)

Issue:
Buggy documentations for how to write your own mdbook plugin
### Problem

The example code in https://rust-lang.github.io/mdBook/for_developers/preprocessors.html#hints-for-implementing-a-preprocessor doesn't actually work:

```rust
cmark(events, &mut buf, None).map(|_| buf).map_err(|err| {
```

That function (from https://docs.rs/pulldown-cmark-to-cmark/latest/pulldown_cmark_to_cmark/fn.cmark.html) only takes two arguments. I went back a few major versions and it still only took two arguments. So presumably it is just extremely outdated (or outright broken). (I have yet to figure out how to properly use the API, so I don't have a specific suggested fix at this point).

### Steps

1. Read docs on writing your own pre-processor.
2. Try to use the example as a starting point.

### Possible Solution(s)

* The code should be updated to work.
* The code should be tested by CI to ensure that it works. Possibly the easiest way to do this is to move it to the nop-example in repo instead.

### Notes

_No response_

### Version

mdbook v0.4.40
pulldown-cmark 0.12.2
pulldown-cmark-to-cmark 18.0.0



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2403de347c..ee69a6c499 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -999,7 +999,7 @@ dependencies = [
  "pathdiff",
  "predicates",
  "pretty_assertions",
- "pulldown-cmark",
+ "pulldown-cmark 0.10.3",
  "regex",
  "select",
  "semver",
@@ -1014,6 +1014,16 @@ dependencies = [
  "warp",
 ]
 
+[[package]]
+name = "mdbook-remove-emphasis"
+version = "0.1.0"
+dependencies = [
+ "mdbook",
+ "pulldown-cmark 0.12.2",
+ "pulldown-cmark-to-cmark",
+ "serde_json",
+]
+
 [[package]]
 name = "memchr"
 version = "2.7.2"
@@ -1421,12 +1431,32 @@ dependencies = [
  "unicase",
 ]
 
+[[package]]
+name = "pulldown-cmark"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
+dependencies = [
+ "bitflags 2.5.0",
+ "memchr",
+ "unicase",
+]
+
 [[package]]
 name = "pulldown-cmark-escape"
 version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3"
 
+[[package]]
+name = "pulldown-cmark-to-cmark"
+version = "18.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e02b63adcb49f2eb675b1694b413b3e9fedbf549dfe2cc98727ad97a0c30650"
+dependencies = [
+ "pulldown-cmark 0.12.2",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.36"
@@ -1598,11 +1628,12 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.117"
+version = "1.0.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index b1e681dd91..be75c90f34 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,6 @@
+[workspace]
+members = [".", "examples/remove-emphasis/mdbook-remove-emphasis"]
+
 [package]
 name = "mdbook"
 version = "0.4.40"
@@ -73,3 +76,9 @@ name = "mdbook"
 [[example]]
 name = "nop-preprocessor"
 test = true
+
+[[example]]
+name = "remove-emphasis"
+path = "examples/remove-emphasis/test.rs"
+crate-type = ["lib"]
+test = true
diff --git a/examples/remove-emphasis/.gitignore b/examples/remove-emphasis/.gitignore
new file mode 100644
index 0000000000..7585238efe
--- /dev/null
+++ b/examples/remove-emphasis/.gitignore
@@ -0,0 +1,1 @@
+book
diff --git a/examples/remove-emphasis/book.toml b/examples/remove-emphasis/book.toml
new file mode 100644
index 0000000000..0e2540007a
--- /dev/null
+++ b/examples/remove-emphasis/book.toml
@@ -0,0 +1,5 @@
+[book]
+title = "remove-emphasis"
+
+[preprocessor.remove-emphasis]
+command = "cargo run --manifest-path=mdbook-remove-emphasis/Cargo.toml --locked"
diff --git a/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml b/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml
new file mode 100644
index 0000000000..7571b18dad
--- /dev/null
+++ b/examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "mdbook-remove-emphasis"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+mdbook = { version = "0.4.40", path = "../../.." }
+pulldown-cmark = { version = "0.12.2", default-features = false }
+pulldown-cmark-to-cmark = "18.0.0"
+serde_json = "1.0.132"
diff --git a/examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs b/examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs
new file mode 100644
index 0000000000..79f5f009f1
--- /dev/null
+++ b/examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs
@@ -0,0 +1,82 @@
+//! This is a demonstration of an mdBook preprocessor which parses markdown
+//! and removes any instances of emphasis.
+
+use mdbook::book::{Book, Chapter};
+use mdbook::errors::Error;
+use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
+use mdbook::BookItem;
+use pulldown_cmark::{Event, Parser, Tag, TagEnd};
+use std::io;
+
+fn main() {
+    let mut args = std::env::args().skip(1);
+    match args.next().as_deref() {
+        Some("supports") => {
+            // Supports all renderers.
+            return;
+        }
+        Some(arg) => {
+            eprintln!("unknown argument: {arg}");
+            std::process::exit(1);
+        }
+        None => {}
+    }
+
+    if let Err(e) = handle_preprocessing() {
+        eprintln!("{}", e);
+        std::process::exit(1);
+    }
+}
+
+struct RemoveEmphasis;
+
+impl Preprocessor for RemoveEmphasis {
+    fn name(&self) -> &str {
+        "remove-emphasis"
+    }
+
+    fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
+        let mut total = 0;
+        book.for_each_mut(|item| {
+            let BookItem::Chapter(ch) = item else {
+                return;
+            };
+            if ch.is_draft_chapter() {
+                return;
+            }
+            match remove_emphasis(&mut total, ch) {
+                Ok(s) => ch.content = s,
+                Err(e) => eprintln!("failed to process chapter: {e:?}"),
+            }
+        });
+        eprintln!("removed {total} emphasis");
+        Ok(book)
+    }
+}
+
+// ANCHOR: remove_emphasis
+fn remove_emphasis(num_removed_items: &mut usize, chapter: &mut Chapter) -> Result<String, Error> {
+    let mut buf = String::with_capacity(chapter.content.len());
+
+    let events = Parser::new(&chapter.content).filter(|e| match e {
+        Event::Start(Tag::Emphasis) | Event::Start(Tag::Strong) => {
+            *num_removed_items += 1;
+            false
+        }
+        Event::End(TagEnd::Emphasis) | Event::End(TagEnd::Strong) => false,
+        _ => true,
+    });
+
+    Ok(pulldown_cmark_to_cmark::cmark(events, &mut buf).map(|_| buf)?)
+}
+// ANCHOR_END: remove_emphasis
+
+pub fn handle_preprocessing() -> Result<(), Error> {
+    let pre = RemoveEmphasis;
+    let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
+
+    let processed_book = pre.run(&ctx, book)?;
+    serde_json::to_writer(io::stdout(), &processed_book)?;
+
+    Ok(())
+}
diff --git a/examples/remove-emphasis/src/SUMMARY.md b/examples/remove-emphasis/src/SUMMARY.md
new file mode 100644
index 0000000000..7390c82896
--- /dev/null
+++ b/examples/remove-emphasis/src/SUMMARY.md
@@ -0,0 +1,3 @@
+# Summary
+
+- [Chapter 1](./chapter_1.md)
diff --git a/examples/remove-emphasis/src/chapter_1.md b/examples/remove-emphasis/src/chapter_1.md
new file mode 100644
index 0000000000..5cb79dddeb
--- /dev/null
+++ b/examples/remove-emphasis/src/chapter_1.md
@@ -0,0 +1,3 @@
+# Chapter 1
+
+This has *light emphasis* and **bold emphasis**.
diff --git a/guide/src/for_developers/preprocessors.md b/guide/src/for_developers/preprocessors.md
index 1ac462561a..1455aceb7a 100644
--- a/guide/src/for_developers/preprocessors.md
+++ b/guide/src/for_developers/preprocessors.md
@@ -68,33 +68,10 @@ The following code block shows how to remove all emphasis from markdown,
 without accidentally breaking the document.
 
 ```rust
-fn remove_emphasis(
-    num_removed_items: &mut usize,
-    chapter: &mut Chapter,
-) -> Result<String> {
-    let mut buf = String::with_capacity(chapter.content.len());
-
-    let events = Parser::new(&chapter.content).filter(|e| {
-        let should_keep = match *e {
-            Event::Start(Tag::Emphasis)
-            | Event::Start(Tag::Strong)
-            | Event::End(Tag::Emphasis)
-            | Event::End(Tag::Strong) => false,
-            _ => true,
-        };
-        if !should_keep {
-            *num_removed_items += 1;
-        }
-        should_keep
-    });
-
-    cmark(events, &mut buf, None).map(|_| buf).map_err(|err| {
-        Error::from(format!("Markdown serialization failed: {}", err))
-    })
-}
+{{#rustdoc_include ../../../examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs:remove_emphasis}}
 ```
 
-For everything else, have a look [at the complete example][example].
+Take a look at the [full example source][emphasis-example] for more details.
 
 ## Implementing a preprocessor with a different language
 
@@ -122,11 +99,10 @@ if __name__ == '__main__':
 ```
 
 
-
+[emphasis-example]: https://github.com/rust-lang/mdBook/tree/master/examples/remove-emphasis/
 [preprocessor-docs]: https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html
 [pc]: https://crates.io/crates/pulldown-cmark
 [pctc]: https://crates.io/crates/pulldown-cmark-to-cmark
-[example]: https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs
 [an example no-op preprocessor]: https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs
 [`CmdPreprocessor::parse_input()`]: https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html#method.parse_input
 [`Book::for_each_mut()`]: https://docs.rs/mdbook/latest/mdbook/book/struct.Book.html#method.for_each_mut


Test patch:
diff --git a/examples/remove-emphasis/test.rs b/examples/remove-emphasis/test.rs
new file mode 100644
index 0000000000..9b614163bb
--- /dev/null
+++ b/examples/remove-emphasis/test.rs
@@ -0,0 +1,13 @@
+use mdbook::MDBook;
+
+#[test]
+fn remove_emphasis_works() {
+    // Tests that the remove-emphasis example works as expected.
+
+    // Workaround for https://github.com/rust-lang/mdBook/issues/1424
+    std::env::set_current_dir("examples/remove-emphasis").unwrap();
+    let book = MDBook::load(".").unwrap();
+    book.build().unwrap();
+    let ch1 = std::fs::read_to_string("book/chapter_1.html").unwrap();
+    assert!(ch1.contains("This has light emphasis and bold emphasis."));
+}