From 1e4c95079cb97b2b02440b21945c6d12741a7d19 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 25 Aug 2022 13:16:45 +0200 Subject: Add support for GFM footnotes --- tests/gfm_footnote.rs | 1530 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1530 insertions(+) create mode 100644 tests/gfm_footnote.rs (limited to 'tests') diff --git a/tests/gfm_footnote.rs b/tests/gfm_footnote.rs new file mode 100644 index 0000000..8c6cc54 --- /dev/null +++ b/tests/gfm_footnote.rs @@ -0,0 +1,1530 @@ +extern crate micromark; +use micromark::{micromark, micromark_with_options, Constructs, Options}; +use pretty_assertions::assert_eq; + +#[test] +fn gfm_footnote() { + let gfm = Options { + constructs: Constructs::gfm(), + ..Options::default() + }; + + assert_eq!( + micromark("A call.[^a]\n\n[^a]: whatevs"), + "

A call.^a

\n", + "should ignore footnotes by default" + ); + + assert_eq!( + micromark_with_options("A call.[^a]\n\n[^a]: whatevs", &gfm), + "

A call.1

+

Footnotes

+
    +
  1. +

    whatevs

    +
  2. +
+
+", + "should support footnotes" + ); + assert_eq!( + micromark_with_options( + "Noot.[^a]\n\n[^a]: dingen", + &Options { + constructs: Constructs::gfm(), + gfm_footnote_label: Some("Voetnoten".to_string()), + gfm_footnote_back_label: Some("Terug naar de inhoud".to_string()), + ..Options::default() + } + ), + "

Noot.1

+

Voetnoten

+
    +
  1. +

    dingen

    +
  2. +
+
+", + "should support `options.gfm_footnote_label`, `options.gfm_footnote_back_label`" + ); + + assert_eq!( + micromark_with_options( + "[^a]\n\n[^a]: b", + &Options { + constructs: Constructs::gfm(), + gfm_footnote_label_tag_name: Some("h1".to_string()), + ..Options::default() + } + ), + "

1

+

Footnotes

+
    +
  1. +

    b

    +
  2. +
+
+", + "should support `options.gfm_footnote_label_tag_name`" + ); + + assert_eq!( + micromark_with_options( + "[^a]\n\n[^a]: b", + &Options { + constructs: Constructs::gfm(), + gfm_footnote_label_attributes: Some("class=\"footnote-heading\"".to_string()), + ..Options::default() + } + ), + "

1

+

Footnotes

+
    +
  1. +

    b

    +
  2. +
+
+", + "should support `options.gfm_footnote_label_attributes`" + ); + + assert_eq!( + micromark_with_options( + "[^a]\n\n[^a]: b", + &Options { + constructs: Constructs::gfm(), + gfm_footnote_clobber_prefix: Some("".to_string()), + ..Options::default() + } + ), + "

1

+

Footnotes

+
    +
  1. +

    b

    +
  2. +
+
+", + "should support `options.gfm_footnote_clobber_prefix`" + ); + + assert_eq!( + micromark_with_options("A paragraph.\n\n[^a]: whatevs", &gfm), + "

A paragraph.

\n", + "should ignore definitions w/o calls" + ); + + assert_eq!( + micromark_with_options("a[^b]", &gfm), + "

a[^b]

", + "should ignore calls w/o definitions" + ); + + assert_eq!( + micromark_with_options("a[^b]\n\n[^b]: c\n[^b]: d", &gfm), + "

a1

+

Footnotes

+
    +
  1. +

    c

    +
  2. +
+
+", + "should use the first of duplicate definitions" + ); + + assert_eq!( + micromark_with_options("a[^b], c[^b]\n\n[^b]: d", &gfm), + "

a1, c1

+

Footnotes

+
    +
  1. +

    d 2

    +
  2. +
+
+", + "should supports multiple calls to the same definition" + ); + + assert_eq!( + micromark_with_options("![^a](b)", &gfm), + "

!^a

", + "should not support images starting w/ `^` (but see it as a link?!, 1)" + ); + + assert_eq!( + micromark_with_options("![^a][b]\n\n[b]: c", &gfm), + "

!^a

\n", + "should not support images starting w/ `^` (but see it as a link?!, 2)" + ); + + assert_eq!( + micromark_with_options("[^]()", &gfm), + "

^

", + "should support an empty link with caret" + ); + + assert_eq!( + micromark_with_options("![^]()", &gfm), + "

!^

", + "should support an empty image with caret (as link)" + ); + + // + assert_eq!( + micromark_with_options("Call.[^a\\+b].\n\n[^a\\+b]: y", &gfm), + "

Call.1.

+

Footnotes

+
    +
  1. +

    y

    +
  2. +
+
+", + "should support a character escape in a call / definition" + ); + + assert_eq!( + micromark_with_options("Call.[^a©b].\n\n[^a©b]: y", &gfm), + "

Call.1.

+

Footnotes

+
    +
  1. +

    y

    +
  2. +
+
+", + "should support a character reference in a call / definition" + ); + + // + // + assert_eq!( + micromark_with_options("Call.[^a\\]b].\n\n[^a\\]b]: y", &gfm), + "

Call.1.

+

Footnotes

+
    +
  1. +

    y

    +
  2. +
+
+", + "should support a useful character escape in a call / definition" + ); + + assert_eq!( + micromark_with_options("Call.[^a[b].\n\n[^a[b]: y", &gfm), + "

Call.1.

+

Footnotes

+
    +
  1. +

    y

    +
  2. +
+
+", + "should support a useful character reference in a call / definition" + ); + + assert_eq!( + micromark_with_options("Call.[^a\\+b].\n\n[^a+b]: y", &gfm), + "

Call.[^a+b].

\n", + "should match calls to definitions on the source of the label, not on resolved escapes" + ); + + assert_eq!( + micromark_with_options("Call.[^a[b].\n\n[^a\\[b]: y", &gfm), + "

Call.[^a[b].

\n", + "should match calls to definitions on the source of the label, not on resolved references" + ); + + assert_eq!( + micromark_with_options("[^1].\n\n[^1]: a\nb", &gfm), + "

1.

+

Footnotes

+
    +
  1. +

    a +b

    +
  2. +
+
+", + "should support lazyness (1)" + ); + + assert_eq!( + micromark_with_options("[^1].\n\n> [^1]: a\nb", &gfm), + "

1.

+
+
+

Footnotes

+
    +
  1. +

    a +b

    +
  2. +
+
+", + "should support lazyness (2)" + ); + + assert_eq!( + micromark_with_options("[^1].\n\n> [^1]: a\n> b", &gfm), + "

1.

+
+
+

Footnotes

+
    +
  1. +

    a +b

    +
  2. +
+
+", + "should support lazyness (3)" + ); + + assert_eq!( + micromark_with_options("[^1].\n\n[^1]: a\n\n > b", &gfm), + "

1.

+

Footnotes

+
    +
  1. +

    a

    +
    +

    b

    +
    + +
  2. +
+
+", + "should support lazyness (4)" + ); + + // 999 `x` characters. + let max = "x".repeat(999); + + assert_eq!( + micromark_with_options(format!("Call.[^{}].\n\n[^{}]: y", max, max).as_str(), &gfm), + format!("

Call.1.

+

Footnotes

+
    +
  1. +

    y

    +
  2. +
+
+", max, max, max, max), + "should support 999 characters in a call / definition" + ); + + assert_eq!( + micromark_with_options( + format!("Call.[^a{}].\n\n[^a{}]: y", max, max).as_str(), + &gfm + ), + format!("

Call.[^a{}].

\n

[^a{}]: y

", max, max), + "should not support 1000 characters in a call / definition" + ); + + assert_eq!( + micromark_with_options( + r###"a![i](#) +a\![i](#) +a![i][] +a![^1] +[^1] +^1] + +[^1]: b + +[i]: c"###, + &gfm + ), + r###"

ai +a!i +ai +a!1 +1 +^1]

+

Footnotes

+
    +
  1. +

    b 2

    +
  2. +
+
+"###, + "should match bang/caret interplay like GitHub" + ); + + assert_eq!( + micromark_with_options("a![^1]", &gfm), + "

a![^1]

", + "should match bang/caret interplay (undefined) like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"a![^1] + +[^1]: b +"###, + &gfm + ), + r###"

a!1

+

Footnotes

+
    +
  1. +

    b

    +
  2. +
+
+"###, + "should match bang/caret like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Calls may not be empty: [^]. + +Calls cannot contain whitespace only: [^ ]. + +Calls cannot contain whitespace at all: [^ ], [^ ], [^ +]. + +Calls can contain other characters, such as numbers [^1234567890], or [^^] +even another caret. + +[^]: empty + +[^ ]: space + +[^ ]: tab + +[^ +]: line feed + +[^1234567890]: numbers + +[^^]: caret +"###, + &gfm + ), + r###"

Calls may not be empty: ^.

+

Calls cannot contain whitespace only: ^ .

+

Calls cannot contain whitespace at all: ^ , ^ , ^ +.

+

Calls can contain other characters, such as numbers 1, or 2 +even another caret.

+

^ +: line feed

+

Footnotes

+
    +
  1. +

    numbers

    +
  2. +
  3. +

    caret

    +
  4. +
+
+"###, + "should match calls like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^*emphasis*] + +[^**strong**] + +[^`code`] + +[^www.example.com] + +[^https://example.com] + +[^://example.com] + +[^[link](#)] + +[^![image](#)] + +[^*emphasis*]: a + +[^**strong**]: a + +[^`code`]: a + +[^www.example.com]: a + +[^https://example.com]: a + +[^://example.com]: a + +[^[link](#)]: a + +[^![image](#)]: a +"###, + &gfm + ), + // Note: + // * GH does not support colons. + // See: + // Here identifiers that include colons *do* work (so they’re added below). + // * GH does not support footnote-like brackets around an image. + // See: + // Here images are fine. + r###"

1

+

2

+

3

+

4

+

5

+

6

+

[^link]

+

[^image]

+

[^link]: a

+

[^image]: a

+

Footnotes

+
    +
  1. +

    a

    +
  2. +
  3. +

    a

    +
  4. +
  5. +

    a

    +
  6. +
  7. +

    a

    +
  8. +
  9. +

    a

    +
  10. +
  11. +

    a

    +
  12. +
+
+"###, + "should match construct identifiers like GitHub (except for its bugs)" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1][^2][^3][^4] + +> [^1]: Defined in a block quote. +> +> More. +[^2]: Directly after a block quote. + +* [^3]: Defined in a list item. + + More. +[^4]: Directly after a list item. +"###, + &gfm + ), + r###"

Call1234

+
+

More.

+
+
    +
  • +

    More.

    +
  • +
+

Footnotes

+
    +
  1. +

    Defined in a block quote.

    +
  2. +
  3. +

    Directly after a block quote.

    +
  4. +
  5. +

    Defined in a list item.

    +
  6. +
  7. +

    Directly after a list item.

    +
  8. +
+
+"###, + "should match containers like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^1][^2][^3][^4] + +[^1]: Paragraph +…continuation + +# Heading + +[^2]: Paragraph +…continuation + + “code”, which is paragraphs… + + …because of the indent! + +[^3]: Paragraph +…continuation + +> block quote + +[^4]: Paragraph +…continuation + +- list +"###, + &gfm + ), + r###"

1234

+

Heading

+
+

block quote

+
+
    +
  • list
  • +
+

Footnotes

+
    +
  1. +

    Paragraph +…continuation

    +
  2. +
  3. +

    Paragraph +…continuation

    +

    “code”, which is paragraphs…

    +

    …because of the indent!

    +
  4. +
  5. +

    Paragraph +…continuation

    +
  6. +
  7. +

    Paragraph +…continuation

    +
  8. +
+
+"###, + "should match continuation like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1][^2][^3][^4][^5]. + +[^1]: + --- + +[^2]: + Paragraph. + +[^3]: +Lazy? + +[^4]: + + Another blank. + +[^5]: + +Lazy! +"###, + &gfm + ), + r###"

Call12345.

+

Lazy?

+

Lazy!

+

Footnotes

+
    +
  1. +
    + +
  2. +
  3. +

    Paragraph.

    +
  4. +
  5. + +
  6. +
  7. +

    Another blank.

    +
  8. +
  9. + +
  10. +
+
+"###, + "should match definitions initial blank like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Note![^0][^1][^2][^3][^4][^5][^6][^7][^8][^9][^10] + +[^0]: alpha + +[^1]: bravo + +[^2]: charlie + indented delta + +[^3]: echo + +[^4]: foxtrot + +[^5]:> golf + +[^6]: > hotel + +[^7]: > india + +[^8]: # juliett + +[^9]: --- + +[^10]:- - - kilo +"###, + &gfm + ), + r###"

Note!1234567891011

+

Footnotes

+
    +
  1. +

    alpha

    +
  2. +
  3. +

    bravo

    +
  4. +
  5. +

    charlie +indented delta

    +
  6. +
  7. +

    echo

    +
  8. +
  9. +

    foxtrot

    +
  10. +
  11. +
    +

    golf

    +
    + +
  12. +
  13. +
    +

    hotel

    +
    + +
  14. +
  15. +
    +

    india

    +
    + +
  16. +
  17. +

    juliett

    + +
  18. +
  19. +
    + +
  20. +
  21. +
      +
    • +
        +
      • +
          +
        • kilo
        • +
        +
      • +
      +
    • +
    + +
  22. +
+
+"###, + "should match definitions like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1][^1] + +[^1]: Recursion[^1][^1] +"###, + &gfm + ), + r###"

Call11

+

Footnotes

+
    +
  1. +

    Recursion11 2 3 4

    +
  2. +
+
+"###, + "should match duplicate calls and recursion like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1] + +[^1]: a + +[^1]: b +"###, + &gfm + ), + r###"

Call1

+

Footnotes

+
    +
  1. +

    a

    +
  2. +
+
+"###, + "should match duplicate definitions like GitHub" + ); + + // Note: + // * GH “supports” footnotes inside links. + // This breaks an HTML parser, as it is not allowed. + // See: + // CommonMark has mechanisms in place to prevent links in links. + // These mechanisms are in place here too. + assert_eq!( + micromark_with_options( + r###"*emphasis[^1]* + +**strong[^2]** + +`code[^3]` + +![image[^4]](#) + +[link[^5]](#) + +[^1]: a + +[^2]: b + +[^3]: c + +[^4]: d + +[^5]: e +"###, + &gfm + ), + r###"

emphasis1

+

strong2

+

code[^3]

+

image

+

[link4](#)

+

Footnotes

+
    +
  1. +

    a

    +
  2. +
  3. +

    b

    +
  4. +
  5. +

    d

    +
  6. +
  7. +

    e

    +
  8. +
+
+"###, + "should match footnotes in constructs like GitHub (without the bugs)" + ); + + assert_eq!( + micromark_with_options( + r###"What are these![^1], ![^2][], and ![this][^3]. + +[^1]: a + +[^2]: b + +[^3]: c +"###, + &gfm + ), + r###"

What are these!1, !2[], and ![this]3.

+

Footnotes

+
    +
  1. +

    a

    +
  2. +
  3. +

    b

    +
  4. +
  5. +

    c

    +
  6. +
+
+"###, + "should match images/footnotes like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^0][^1][^2][^3][^4][^5] + +[^0]: Paragraph +…continuation +[^1]: Another +[^2]: Paragraph +…continuation +# Heading +[^3]: Paragraph +…continuation + “code”, which is paragraphs… + + …because of the indent! +[^4]: Paragraph +…continuation +> block quote +[^5]: Paragraph +…continuation +* list +"###, + &gfm + ), + r###"

123456

+

Heading

+
+

block quote

+
+
    +
  • list
  • +
+

Footnotes

+
    +
  1. +

    Paragraph +…continuation

    +
  2. +
  3. +

    Another

    +
  4. +
  5. +

    Paragraph +…continuation

    +
  6. +
  7. +

    Paragraph +…continuation +“code”, which is paragraphs…

    +

    …because of the indent!

    +
  8. +
  9. +

    Paragraph +…continuation

    +
  10. +
  11. +

    Paragraph +…continuation

    +
  12. +
+
+"###, + "should match interrupt like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"What are these[^1], [^2][], and [this][^3]. + +[^1]: a + +[^2]: b + +[^3]: c +"###, + &gfm + ), + r###"

What are these1, 2[], and [this]3.

+

Footnotes

+
    +
  1. +

    a

    +
  2. +
  3. +

    b

    +
  4. +
  5. +

    c

    +
  6. +
+
+"###, + "should match links/footnotes like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^1][^2][^3][^4] + +[^1]: Paragraph + + +# Heading + + +[^2]: Paragraph + + + “code”, which is paragraphs… + + + …because of the indent! + + +[^3]: Paragraph + + +> block quote + + +[^4]: Paragraph + + +- list +"###, + &gfm + ), + r###"

1234

+

Heading

+
+

block quote

+
+
    +
  • list
  • +
+

Footnotes

+
    +
  1. +

    Paragraph

    +
  2. +
  3. +

    Paragraph

    +

    “code”, which is paragraphs…

    +

    …because of the indent!

    +
  4. +
  5. +

    Paragraph

    +
  6. +
  7. +

    Paragraph

    +
  8. +
+
+"###, + "should match many blank lines/no indent like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^1][^2][^3][^4] + +[^1]: Paragraph + + + # Heading + + +[^2]: Paragraph + + + code + + + more code + + +[^3]: Paragraph + + + > block quote + + +[^4]: Paragraph + + + - list +"###, + &gfm + ), + r###"

1234

+

Footnotes

+
    +
  1. +

    Paragraph

    +

    Heading

    + +
  2. +
  3. +

    Paragraph

    +
    code
    +
    +
    +more code
    +
    + +
  4. +
  5. +

    Paragraph

    +
    +

    block quote

    +
    + +
  6. +
  7. +

    Paragraph

    +
      +
    • list
    • +
    + +
  8. +
+
+"###, + "should match many blank lines like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Note![^1][^2][^3][^4] + +- [^1]: Paragraph + +> [^2]: Paragraph + +[^3]: [^4]: Paragraph +"###, + &gfm + ), + r###"

Note!1234

+
    +
  • +
+
+
+

Footnotes

+
    +
  1. +

    Paragraph

    +
  2. +
  3. +

    Paragraph

    +
  4. +
  5. + +
  6. +
  7. +

    Paragraph

    +
  8. +
+
+"###, + "should match nest like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^1][^2][^3][^4] + +[^1]: Paragraph + +# Heading + +[^2]: Paragraph + + “code”, which is paragraphs… + + …because of the indent! + +[^3]: Paragraph + +> block quote + +[^4]: Paragraph + +- list +"###, + &gfm + ), + r###"

1234

+

Heading

+
+

block quote

+
+
    +
  • list
  • +
+

Footnotes

+
    +
  1. +

    Paragraph

    +
  2. +
  3. +

    Paragraph

    +

    “code”, which is paragraphs…

    +

    …because of the indent!

    +
  4. +
  5. +

    Paragraph

    +
  6. +
  7. +

    Paragraph

    +
  8. +
+
+"###, + "should match normal blank lines/no indent like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"[^1][^2][^3][^4] + +[^1]: Paragraph + + # Heading + +[^2]: Paragraph + + code + + more code + +[^3]: Paragraph + + > block quote + +[^4]: Paragraph + + - list +"###, + &gfm + ), + r###"

1234

+

Footnotes

+
    +
  1. +

    Paragraph

    +

    Heading

    + +
  2. +
  3. +

    Paragraph

    +
    code
    +
    +more code
    +
    + +
  4. +
  5. +

    Paragraph

    +
    +

    block quote

    +
    + +
  6. +
  7. +

    Paragraph

    +
      +
    • list
    • +
    + +
  8. +
+
+"###, + "should match normal blank lines like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Here is a footnote reference,[^1] and another.[^longnote] + +[^1]: Here is the footnote. + +[^longnote]: Here’s one with multiple blocks. + + Subsequent paragraphs are indented to show that they +belong to the previous footnote. + + { some.code } + + The whole paragraph can be indented, or just the first + line. In this way, multi-paragraph footnotes work like + multi-paragraph list items. + +This paragraph won’t be part of the note, because it +isn’t indented. +"###, + &gfm + ), + r###"

Here is a footnote reference,1 and another.2

+

This paragraph won’t be part of the note, because it +isn’t indented.

+

Footnotes

+
    +
  1. +

    Here is the footnote.

    +
  2. +
  3. +

    Here’s one with multiple blocks.

    +

    Subsequent paragraphs are indented to show that they +belong to the previous footnote.

    +
    { some.code }
    +
    +

    The whole paragraph can be indented, or just the first +line. In this way, multi-paragraph footnotes work like +multi-paragraph list items.

    +
  4. +
+
+"###, + "should match pandoc like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1][^2][^3][^4][^5][^6][^7][^8][^9][^10][^11][^12]. + + [^1]: 5 + + [^2]: 4 + + [^3]: 3 + + [^4]: 2 + + [^5]: 1 + +[^6]: 0 + +*** + + [^7]: 3 + + 5 + + [^8]: 3 + + 4 + + [^9]: 3 + + 3 + + [^10]: 2 + + 5 + + [^11]: 2 + + 4 + + [^12]: 2 + + 3 +"###, + &gfm + ), + r###"

Call[^1][^2]12345678910.

+
 [^1]: 5
+
+[^2]: 4
+
+
+

3

+

3

+

Footnotes

+
    +
  1. +

    3

    +
  2. +
  3. +

    2

    +
  4. +
  5. +

    1

    +
  6. +
  7. +

    0

    +
  8. +
  9. +

    3

    +

    5

    +
  10. +
  11. +

    3

    +

    4

    +
  12. +
  13. +

    3

    +
  14. +
  15. +

    2

    +

    5

    +
  16. +
  17. +

    2

    +

    4

    +
  18. +
  19. +

    2

    +
  20. +
+
+"###, + "should match prefix before like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Call[^1][^2][^3][^4][^5][^6][^7][^8][^9]. + +[^1]: a + + 8 + +[^2]: a + + 7 + +[^3]: a + + 6 + +[^4]: a + + 5 + +[^5]: a + + 4 + +[^6]: a + + 3 + +[^7]: a + + 2 + +[^8]: a + + 1 + +[^9]: a + +0 +"###, + &gfm + ), + r###"

Call123456789.

+

3

+

2

+

1

+

0

+

Footnotes

+
    +
  1. +

    a

    +
    8
    +
    + +
  2. +
  3. +

    a

    +

    7

    +
  4. +
  5. +

    a

    +

    6

    +
  6. +
  7. +

    a

    +

    5

    +
  8. +
  9. +

    a

    +

    4

    +
  10. +
  11. +

    a

    +
  12. +
  13. +

    a

    +
  14. +
  15. +

    a

    +
  16. +
  17. +

    a

    +
  18. +
+
+"###, + "should match prefix like GitHub" + ); + + assert_eq!( + micromark_with_options( + r###"Here is a short reference,[1], a collapsed one,[2][], and a full [one][3]. + +[1]: a + +[2]: b + +[3]: c +"###, + &gfm + ), + r###"

Here is a short reference,1, a collapsed one,2, and a full one.

+"###, + "should match references and definitions like GitHub" + ); +} -- cgit