diff --git a/src/utils/mod.rs b/src/utils/mod.rs index fc8584ee..0ca0482f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -235,10 +235,10 @@ pub fn render_markdown_with_path( // `count` is the number of references to this footnote (used for multiple // linkbacks, and checking for unused footnotes). let mut footnote_numbers = HashMap::new(); - // This is a list of (name, Vec) + // This is a map of name -> Vec // `name` is the name of the footnote. // The events list is the list of events needed to build the footnote definition. - let mut footnote_defs = Vec::new(); + let mut footnote_defs = HashMap::new(); // The following are used when currently processing a footnote definition. // @@ -268,7 +268,16 @@ pub fn render_markdown_with_path( Event::End(TagEnd::FootnoteDefinition) => { let def_events = std::mem::take(&mut in_footnote); let name = std::mem::take(&mut in_footnote_name); - footnote_defs.push((name, def_events)); + + if footnote_defs.contains_key(&name) { + log::warn!( + "footnote `{name}` in {} defined multiple times - \ + not updating to new definition", + path.map_or_else(|| Cow::from(""), |p| p.to_string_lossy()) + ); + } else { + footnote_defs.insert(name, def_events); + } None } Event::FootnoteReference(name) => { @@ -304,7 +313,12 @@ pub fn render_markdown_with_path( html::push_html(&mut body, events); if !footnote_defs.is_empty() { - add_footnote_defs(&mut body, path, footnote_defs, &footnote_numbers); + add_footnote_defs( + &mut body, + path, + footnote_defs.into_iter().collect(), + &footnote_numbers, + ); } body diff --git a/tests/testsuite/markdown.rs b/tests/testsuite/markdown.rs index 50471826..c4bfbd07 100644 --- a/tests/testsuite/markdown.rs +++ b/tests/testsuite/markdown.rs @@ -1,6 +1,7 @@ //! Tests for special markdown rendering. use crate::prelude::*; +use snapbox::file; // Checks custom header id and classes. #[test] @@ -17,46 +18,21 @@ fn custom_header_attributes() { #[test] fn footnotes() { BookTest::from_dir("markdown/footnotes") - .check_main_file("book/footnotes.html", str![[r##" -

Footnote tests

-

Footnote example1, or with a word2.

-

There are multiple references to word2.

-

Footnote without a paragraph3

-

Footnote with multiple paragraphs4

-

Footnote name with wacky characters5

-

Testing when referring to something earlier.6

-
-
  1. -

    This is a footnote. ↩2

    -
  2. -
  3. -

    A longer footnote. -With multiple lines. Link to other. -With a reference inside.1 ↩2

    -
  4. -
  5. -
      -
    1. Item one -
        -
      1. Sub-item
      2. -
      -
    2. -
    3. Item two
    4. -
    -
  6. -
  7. -

    One

    -

    Two

    -

    Three

    -
  8. -
  9. -

    Testing footnote id with special characters.

    -
  10. -
  11. -

    This is defined before it is referred to.

    -
  12. -
-"##]]); + .run("build", |cmd| { + cmd.expect_stderr(str![[r#" +[TIMESTAMP] [INFO] (mdbook::book): Book building has started +[TIMESTAMP] [INFO] (mdbook::book): Running the html backend +[TIMESTAMP] [WARN] (mdbook::utils): footnote `multiple-definitions` in defined multiple times - not updating to new definition +[TIMESTAMP] [WARN] (mdbook::utils): footnote `unused` in `` is defined but not referenced +[TIMESTAMP] [WARN] (mdbook::utils): footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition +[TIMESTAMP] [WARN] (mdbook::utils): footnote `unused` in `footnotes.md` is defined but not referenced + +"#]]); + }) + .check_main_file( + "book/footnotes.html", + file!["markdown/footnotes/expected/footnotes.html"], + ); } // Basic table test. diff --git a/tests/testsuite/markdown/footnotes/expected/footnotes.html b/tests/testsuite/markdown/footnotes/expected/footnotes.html new file mode 100644 index 00000000..8904cb06 --- /dev/null +++ b/tests/testsuite/markdown/footnotes/expected/footnotes.html @@ -0,0 +1,46 @@ +

Footnote tests

+

Footnote example1, or with a word2.

+

There are multiple references to word2.

+

Footnote without a paragraph3

+

Footnote with multiple paragraphs4

+

Footnote name with wacky characters5

+

Testing when referring to something earlier.6

+

Footnote that is defined multiple times.7

+

And another8 that references the duplicate again.7

+
+
  1. +

    This is a footnote. ↩2

    +
  2. +
  3. +

    A longer footnote. +With multiple lines. Link to other. +With a reference inside.1 ↩2

    +
  4. +
  5. +
      +
    1. Item one +
        +
      1. Sub-item
      2. +
      +
    2. +
    3. Item two
    4. +
    +
  6. +
  7. +

    One

    +

    Two

    +

    Three

    +
  8. +
  9. +

    Testing footnote id with special characters.

    +
  10. +
  11. +

    This is defined before it is referred to.

    +
  12. +
  13. +

    This is the first definition of the footnote with tag multiple-definitions ↩2

    +
  14. +
  15. +

    Footnote between duplicates.

    +
  16. +
\ No newline at end of file diff --git a/tests/testsuite/markdown/footnotes/src/footnotes.md b/tests/testsuite/markdown/footnotes/src/footnotes.md index f1fbacca..d7719a74 100644 --- a/tests/testsuite/markdown/footnotes/src/footnotes.md +++ b/tests/testsuite/markdown/footnotes/src/footnotes.md @@ -35,3 +35,13 @@ Footnote name with wacky characters[^"wacky"] [^"wacky"]: Testing footnote id with special characters. Testing when referring to something earlier.[^define-before-use] + +Footnote that is defined multiple times.[^multiple-definitions] + +[^multiple-definitions]: This is the first definition of the footnote with tag multiple-definitions + +And another[^in-between] that references the duplicate again.[^multiple-definitions] + +[^in-between]: Footnote between duplicates. + +[^multiple-definitions]: This is the second definition of the footnote with tag multiple-definitions