mirror of
https://github.com/jgm/pandoc.git
synced 2026-08-28 17:20:47 +08:00
Add BBCode writer (#11242)
`bbcode` is now supported as an output format, as well as variants `bbcode_fluxbb` (FluxBB), `bbcode_phpbb` (phpBB), `bbcode_steam` (Hubzilla), `bbcode_hubzilla` (Hubzilla), and `bbcode_xenforo` (xenForo). [API change] Adds a new module Text.Pandoc.Writers.BBCode, exporting a number of functions. Also exports `writeBBCode`, `writeBBCodeSteam`, `writeBBCodeFluxBB`, `writeBBCodePhpBB`, `writeBBCodeHubzilla`, `writeBBCodeXenforo` from Text.Pandoc.Writers.
This commit is contained in:
@@ -275,6 +275,7 @@ tests pandocPath =
|
||||
"vimdoc/headers.markdown" "vimdoc/headers-numbered.vimdoc"
|
||||
]
|
||||
]
|
||||
, testGroup "bbcode" [testGroup "writer" $ writerTests' "bbcode"]
|
||||
]
|
||||
where
|
||||
test' = test pandocPath
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module Tests.Writers.BBCode (tests) where
|
||||
|
||||
import Data.Maybe (isNothing)
|
||||
import Data.Text as T
|
||||
import Test.Tasty
|
||||
import Test.Tasty.HUnit (HasCallStack)
|
||||
import Test.Tasty.QuickCheck
|
||||
import Tests.Helpers
|
||||
import Text.Pandoc
|
||||
import Text.Pandoc.Arbitrary ()
|
||||
import Text.Pandoc.Builder
|
||||
import Text.Pandoc.Shared (tshow)
|
||||
import Text.Read (readMaybe)
|
||||
|
||||
bbcodeDefault
|
||||
, bbcodeSteam
|
||||
, bbcodePhpBB
|
||||
, bbcodeFluxBB
|
||||
, bbcodeHubzilla
|
||||
, bbcodeXenforo ::
|
||||
(ToPandoc a) => a -> Text
|
||||
bbcodeDefault = purely (writeBBCode def) . toPandoc
|
||||
bbcodeSteam = purely (writeBBCodeSteam def) . toPandoc
|
||||
bbcodePhpBB = purely (writeBBCodePhpBB def) . toPandoc
|
||||
bbcodeFluxBB = purely (writeBBCodeFluxBB def) . toPandoc
|
||||
bbcodeHubzilla = purely (writeBBCodeHubzilla def) . toPandoc
|
||||
bbcodeXenforo = purely (writeBBCodeXenforo def) . toPandoc
|
||||
|
||||
infix 4 =:, `steam`, `phpbb`, `fluxbb`, `hubzilla`, `xenforo`
|
||||
(=:)
|
||||
, steam
|
||||
, phpbb
|
||||
, fluxbb
|
||||
, hubzilla
|
||||
, xenforo ::
|
||||
(ToString a, ToPandoc a, HasCallStack) =>
|
||||
String ->
|
||||
(a, Text) ->
|
||||
TestTree
|
||||
(=:) = test bbcodeDefault
|
||||
steam = test bbcodeSteam
|
||||
phpbb = test bbcodePhpBB
|
||||
fluxbb = test bbcodeFluxBB
|
||||
hubzilla = test bbcodeHubzilla
|
||||
xenforo = test bbcodeXenforo
|
||||
|
||||
spanClasses :: [Text] -> Inlines -> Inlines
|
||||
spanClasses cls = spanWith ("", cls, [])
|
||||
|
||||
spanAttrs :: [(Text, Text)] -> Inlines -> Inlines
|
||||
spanAttrs kvList = spanWith ("", [], kvList)
|
||||
|
||||
divClasses :: [Text] -> Blocks -> Blocks
|
||||
divClasses cls = divWith ("", cls, [])
|
||||
|
||||
divAttrs :: [(Text, Text)] -> Blocks -> Blocks
|
||||
divAttrs kvList = divWith ("", [], kvList)
|
||||
|
||||
tests :: [TestTree]
|
||||
tests =
|
||||
[ testGroup
|
||||
"spans classes"
|
||||
[ "left" =: spanClasses ["left"] "foo" =?> "foo"
|
||||
, "center" =: spanClasses ["center"] "foo" =?> "foo"
|
||||
, "right" =: spanClasses ["right"] "foo" =?> "foo"
|
||||
, "spoiler" =: spanClasses ["spoiler"] "foo" =?> "foo"
|
||||
]
|
||||
, testGroup
|
||||
"spans attributes"
|
||||
[ testProperty "incorrect size ignored" . property $ do
|
||||
n <- arbitrary @String
|
||||
let nInt = readMaybe @Int n
|
||||
let actual = bbcodeDefault (spanAttrs [("size", T.pack n)] "foo")
|
||||
pure $ isNothing nInt ==> actual === "foo"
|
||||
, testProperty "size<=0 ignored" . property $ do
|
||||
NonPositive n <- arbitrary @(NonPositive Int)
|
||||
let actual = bbcodeDefault (spanAttrs [("size", tshow n)] "foo")
|
||||
pure $ actual === "foo"
|
||||
, testProperty "size>0" . property $ do
|
||||
Positive n <- arbitrary @(Positive Int)
|
||||
let actual = bbcodeDefault (spanAttrs [("size", tshow n)] "foo")
|
||||
let expected = "[size=" <> tshow n <> "]" <> "foo[/size]"
|
||||
pure $ actual === expected
|
||||
, "size=20" =: spanAttrs [("size", "20")] "foo" =?> "[size=20]foo[/size]"
|
||||
, "color=#AAAAAA"
|
||||
=: spanAttrs [("color", "#AAAAAA")] "foo"
|
||||
=?> "[color=#AAAAAA]foo[/color]"
|
||||
, "spoiler ignored"
|
||||
=: spanAttrs [("spoiler", "name with spaces and ]brackets[]")] "foo"
|
||||
=?> "foo"
|
||||
]
|
||||
, testGroup
|
||||
"divs classes"
|
||||
[ "left"
|
||||
=: divClasses ["left"] (para "foo")
|
||||
=?> "[left]foo[/left]"
|
||||
, "center"
|
||||
=: divClasses ["center"] (para "foo")
|
||||
=?> "[center]foo[/center]"
|
||||
, "right"
|
||||
=: divClasses ["right"] (para "foo")
|
||||
=?> "[right]foo[/right]"
|
||||
, "spoiler"
|
||||
=: divClasses ["spoiler"] (para "foo")
|
||||
=?> "[spoiler]foo[/spoiler]"
|
||||
]
|
||||
, testGroup
|
||||
"divs attributes"
|
||||
[ testProperty "incorrect size ignored" . property $ do
|
||||
n <- arbitrary @String
|
||||
let nInt = readMaybe @Int n
|
||||
let actual = bbcodeDefault (divAttrs [("size", T.pack n)] $ para "foo")
|
||||
pure $ isNothing nInt ==> actual === "foo"
|
||||
, testProperty "size<=0 ignored" . property $ do
|
||||
NonPositive n <- arbitrary @(NonPositive Int)
|
||||
let actual = bbcodeDefault (divAttrs [("size", tshow n)] $ para "foo")
|
||||
pure $ actual === "foo"
|
||||
, testProperty "size>0" . property $ do
|
||||
Positive n <- arbitrary @(Positive Int)
|
||||
let actual = bbcodeDefault (divAttrs [("size", tshow n)] $ para "foo")
|
||||
let expected = "[size=" <> tshow n <> "]" <> "foo[/size]"
|
||||
pure $ actual === expected
|
||||
, "size=20"
|
||||
=: divAttrs [("size", "20")] (para "foo")
|
||||
=?> "[size=20]foo[/size]"
|
||||
, "color=#AAAAAA"
|
||||
=: divAttrs [("color", "#AAAAAA")] (para "foo")
|
||||
=?> "[color=#AAAAAA]foo[/color]"
|
||||
, "spoiler"
|
||||
=: divAttrs
|
||||
[("spoiler", "name with spaces and ]brackets[]")]
|
||||
(para "foo")
|
||||
=?> "[spoiler=name with spaces and brackets]foo[/spoiler]"
|
||||
]
|
||||
, testGroup
|
||||
"default flavor"
|
||||
[ "link"
|
||||
=: link "https://example.com" "title" "label"
|
||||
=?> "[url=https://example.com]label[/url]"
|
||||
, "autolink"
|
||||
=: link "https://example.com" "title" "https://example.com"
|
||||
=?> "[url]https://example.com[/url]"
|
||||
, "email autolink"
|
||||
=: link
|
||||
"mailto:example@example.com"
|
||||
"title"
|
||||
"example@example.com"
|
||||
=?> "[email]example@example.com[/email]"
|
||||
, "named email"
|
||||
=: link "mailto:example@example.com" "title" "example email"
|
||||
=?> "[email=example@example.com]example email[/email]"
|
||||
, "h0" =: header 0 "heading 0" =?> "[u][b]heading 0[/b][/u]"
|
||||
, "h1" =: header 1 "heading 1" =?> "[u][b]heading 1[/b][/u]"
|
||||
, "h2" =: header 2 "heading 2" =?> "[b]heading 2[/b]"
|
||||
, "h3" =: header 3 "heading 3" =?> "[u]heading 3[/u]"
|
||||
, "h4" =: header 4 "heading 4" =?> "heading 4"
|
||||
, "h5" =: header 5 "heading 5" =?> "heading 5"
|
||||
]
|
||||
, testGroup
|
||||
"steam"
|
||||
[ test bbcodeSteam "dename spoiler" $
|
||||
divAttrs [("spoiler", "bar")] (para "foo")
|
||||
=?> ("[spoiler]foo[/spoiler]" :: Text)
|
||||
, testProperty "ordered list styleless" . property $ do
|
||||
let listItems = [para "foo", para "bar", para "baz"]
|
||||
attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
let actual = bbcodeSteam $ orderedListWith attrsRand listItems
|
||||
let expected = "[olist]\n[*]foo\n[*]bar\n[*]baz\n[/olist]"
|
||||
pure $ actual === expected
|
||||
, "h0" `steam` header 0 "heading 0" =?> "[h1]heading 0[/h1]"
|
||||
, "h1" `steam` header 1 "heading 1" =?> "[h1]heading 1[/h1]"
|
||||
, "h2" `steam` header 2 "heading 2" =?> "[h2]heading 2[/h2]"
|
||||
, "h3" `steam` header 3 "heading 3" =?> "[h3]heading 3[/h3]"
|
||||
, "h4" `steam` header 4 "heading 4" =?> "[h3]heading 4[/h3]"
|
||||
, "code"
|
||||
`steam` codeWith ("id", ["haskell"], []) "map (2^) [1..5]"
|
||||
=?> "[noparse]map (2^) [1..5][/noparse]"
|
||||
]
|
||||
, testGroup
|
||||
"phpBB"
|
||||
[ "image"
|
||||
`phpbb` imageWith
|
||||
("id", [], [("width", "100")])
|
||||
"https://example.com"
|
||||
"title"
|
||||
"alt text"
|
||||
=?> "[img]https://example.com[/img]"
|
||||
]
|
||||
, testGroup
|
||||
"FluxBB"
|
||||
[ "image"
|
||||
`fluxbb` imageWith
|
||||
("id", [], [("width", "100")])
|
||||
"https://example.com"
|
||||
"title"
|
||||
"alt text"
|
||||
=?> "[img=alt text]https://example.com[/img]"
|
||||
, testProperty "ordered list" . property $ do
|
||||
let listItems = [para "foo", para "bar", para "baz"]
|
||||
attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
let actual = bbcodeFluxBB $ orderedListWith attrsRand listItems
|
||||
let opening = case attrsRand of
|
||||
(_, LowerAlpha, _) -> "[list=a]"
|
||||
(_, UpperAlpha, _) -> "[list=a]"
|
||||
_ -> "[list=1]"
|
||||
let expected = opening <> "\n[*]foo\n[*]bar\n[*]baz\n[/list]"
|
||||
pure $ actual === expected
|
||||
, "ulist > BlockQuote not rendered"
|
||||
`fluxbb` bulletList [blockQuote (para "foo") <> para "bar"]
|
||||
=?> "[list]\n[*]bar\n[/list]"
|
||||
, "code block"
|
||||
`fluxbb` codeBlockWith
|
||||
("id", ["haskell"], [])
|
||||
( T.intercalate "\n" $
|
||||
[ "vals ="
|
||||
, " take 10"
|
||||
, " . filter (\\x -> (x - 5) `mod` 3 == 0)"
|
||||
, " $ map (2 ^) [1 ..]"
|
||||
]
|
||||
)
|
||||
=?> T.intercalate
|
||||
"\n"
|
||||
[ "[code]vals ="
|
||||
, " take 10"
|
||||
, " . filter (\\x -> (x - 5) `mod` 3 == 0)"
|
||||
, " $ map (2 ^) [1 ..]"
|
||||
, "[/code]"
|
||||
]
|
||||
]
|
||||
, testGroup
|
||||
"Hubzilla"
|
||||
[ "unordered list"
|
||||
`hubzilla` bulletList [para "foo", para "bar", para "baz"]
|
||||
=?> "[ul]\n[*]foo\n[*]bar\n[*]baz\n[/ul]"
|
||||
, testProperty "ordered list" . property $ do
|
||||
let listItems = [para "foo", para "bar", para "baz"]
|
||||
attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
let actual = bbcodeHubzilla $ orderedListWith attrsRand listItems
|
||||
let (opening, closing) = case attrsRand of
|
||||
(_, Decimal, _) -> ("[list=1]", "[/list]")
|
||||
(_, DefaultStyle, _) -> ("[ol]", "[/ol]")
|
||||
(_, Example, _) -> ("[ol]", "[/ol]")
|
||||
(_, LowerAlpha, _) -> ("[list=a]", "[/list]")
|
||||
(_, UpperAlpha, _) -> ("[list=A]", "[/list]")
|
||||
(_, LowerRoman, _) -> ("[list=i]", "[/list]")
|
||||
(_, UpperRoman, _) -> ("[list=I]", "[/list]")
|
||||
let expected =
|
||||
opening <> "\n[*]foo\n[*]bar\n[*]baz\n" <> closing
|
||||
pure $ actual === expected
|
||||
, "definition list"
|
||||
`hubzilla` definitionList
|
||||
[ ("term_foo", [para "def_foo1", para "def_foo2"])
|
||||
, ("term_bar", [para "def_bar1", para "def_bar2"])
|
||||
, ("term_baz", [para "def_baz1", para "def_baz2"])
|
||||
]
|
||||
=?> mconcat
|
||||
[ "[dl terms=\"b\"]\n"
|
||||
, "[*= term_foo]\ndef_foo1\ndef_foo2\n"
|
||||
, "[*= term_bar]\ndef_bar1\ndef_bar2\n"
|
||||
, "[*= term_baz]\ndef_baz1\ndef_baz2\n"
|
||||
, "[/dl]"
|
||||
]
|
||||
, "h0" `hubzilla` header 0 "heading 0" =?> "[h1]heading 0[/h1]"
|
||||
, "h1" `hubzilla` header 1 "heading 1" =?> "[h1]heading 1[/h1]"
|
||||
, "h2" `hubzilla` header 2 "heading 2" =?> "[h2]heading 2[/h2]"
|
||||
, "h3" `hubzilla` header 3 "heading 3" =?> "[h3]heading 3[/h3]"
|
||||
, "h4" `hubzilla` header 4 "heading 4" =?> "[h4]heading 4[/h4]"
|
||||
, "h5" `hubzilla` header 5 "heading 5" =?> "[h5]heading 5[/h5]"
|
||||
, "h6" `hubzilla` header 6 "heading 6" =?> "[h6]heading 6[/h6]"
|
||||
, "h7" `hubzilla` header 7 "heading 7" =?> "[h6]heading 7[/h6]"
|
||||
, "link"
|
||||
`hubzilla` link "https://example.com" "title" "label"
|
||||
=?> "[url=https://example.com]label[/url]"
|
||||
, "autolink"
|
||||
`hubzilla` link "https://example.com" "title" "https://example.com"
|
||||
=?> "[url]https://example.com[/url]"
|
||||
, "email autolink"
|
||||
`hubzilla` link
|
||||
"mailto:example@example.com"
|
||||
"title"
|
||||
"example@example.com"
|
||||
=?> "[url=mailto:example@example.com]example@example.com[/url]"
|
||||
, "named email"
|
||||
`hubzilla` link "mailto:example@example.com" "title" "example email"
|
||||
=?> "[url=mailto:example@example.com]example email[/url]"
|
||||
, "inline code"
|
||||
`hubzilla` ( "inline code: "
|
||||
<> codeWith ("id", ["haskell"], []) "map (2^) [1..5]"
|
||||
)
|
||||
=?> "inline code: [code]map (2^) [1..5][/code]"
|
||||
, "font"
|
||||
`hubzilla` divAttrs [("font", "serif")] (para "foo")
|
||||
=?> "[font=serif]foo[/font]"
|
||||
]
|
||||
, testGroup
|
||||
"xenForo"
|
||||
[ "unordered list"
|
||||
`xenforo` bulletList [para "foo", para "bar", para "baz"]
|
||||
=?> "[list]\n[*]foo\n[*]bar\n[*]baz\n[/list]"
|
||||
, testProperty "ordered list styleless" . property $ do
|
||||
let listItems = [para "foo", para "bar", para "baz"]
|
||||
attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary
|
||||
let actual = bbcodeXenforo $ orderedListWith attrsRand listItems
|
||||
let expected = "[list=1]\n[*]foo\n[*]bar\n[*]baz\n[/list]"
|
||||
pure $ actual === expected
|
||||
, "h0" `xenforo` header 0 "heading 0" =?> "[heading=1]heading 0[/heading]"
|
||||
, "h1" `xenforo` header 1 "heading 1" =?> "[heading=1]heading 1[/heading]"
|
||||
, "h2" `xenforo` header 2 "heading 2" =?> "[heading=2]heading 2[/heading]"
|
||||
, "h3" `xenforo` header 3 "heading 3" =?> "[heading=3]heading 3[/heading]"
|
||||
, "h4" `xenforo` header 4 "heading 4" =?> "[heading=4]heading 4[/heading]"
|
||||
, "link"
|
||||
`xenforo` link "https://example.com" "title" "label"
|
||||
=?> "[url=https://example.com]label[/url]"
|
||||
, "autolink"
|
||||
`xenforo` link "https://example.com" "title" "https://example.com"
|
||||
=?> "[url]https://example.com[/url]"
|
||||
, "email autolink"
|
||||
`xenforo` link
|
||||
"mailto:example@example.com"
|
||||
"title"
|
||||
"example@example.com"
|
||||
=?> "[email]example@example.com[/email]"
|
||||
, "named email"
|
||||
`xenforo` link "mailto:example@example.com" "title" "example email"
|
||||
=?> "[email=example@example.com]example email[/email]"
|
||||
, "inline code"
|
||||
`xenforo` ( "inline code: "
|
||||
<> codeWith ("id", ["haskell"], []) "map (2^) [1..5]"
|
||||
)
|
||||
=?> "inline code: [icode]map (2^) [1..5][/icode]"
|
||||
, "font"
|
||||
`xenforo` divAttrs [("font", "serif")] (para "foo")
|
||||
=?> "[font=serif]foo[/font]"
|
||||
, "inline spoiler"
|
||||
`xenforo` ("It was " <> spanClasses ["spoiler"] ("DNS") <> "!")
|
||||
=?> "It was [ispoiler]DNS[/ispoiler]!"
|
||||
, "image w=50% h=50%"
|
||||
`xenforo` imageWith
|
||||
("", [], [("width", "50%"), ("height", "50%")])
|
||||
"https://example.com"
|
||||
"title text"
|
||||
"alt text"
|
||||
=?> "[img alt=\"alt text\" title=\"title text\" width=50%]https://example.com[/img]"
|
||||
, "image w=50 h=50"
|
||||
`xenforo` imageWith
|
||||
("", [], [("width", "50"), ("height", "50")])
|
||||
"https://example.com"
|
||||
""
|
||||
""
|
||||
=?> "[img]https://example.com[/img]"
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
Simple table with caption:
|
||||
|
||||
Demonstration of simple table syntax.
|
||||
[table]
|
||||
[tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr]
|
||||
[tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr]
|
||||
[tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr]
|
||||
[tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Simple table without caption:
|
||||
|
||||
[table]
|
||||
[tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr]
|
||||
[tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr]
|
||||
[tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr]
|
||||
[tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Simple table indented two spaces:
|
||||
|
||||
Demonstration of simple table syntax.
|
||||
[table]
|
||||
[tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr]
|
||||
[tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr]
|
||||
[tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr]
|
||||
[tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Multiline table with caption:
|
||||
|
||||
Here's the caption. It may span multiple lines.
|
||||
[table]
|
||||
[tr][th]Centered Header[/th][th]Left Aligned[/th][th]Right Aligned[/th][th]Default aligned[/th][/tr]
|
||||
[tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr]
|
||||
[tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Multiline table without caption:
|
||||
|
||||
[table]
|
||||
[tr][th]Centered Header[/th][th]Left Aligned[/th][th]Right Aligned[/th][th]Default aligned[/th][/tr]
|
||||
[tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr]
|
||||
[tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Table without column headers:
|
||||
|
||||
[table]
|
||||
[tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr]
|
||||
[tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr]
|
||||
[tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr]
|
||||
[/table]
|
||||
|
||||
Multiline table without column headers:
|
||||
|
||||
[table]
|
||||
[tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr]
|
||||
[tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr]
|
||||
[/table]
|
||||
@@ -50,6 +50,7 @@ import qualified Tests.Writers.RST
|
||||
import qualified Tests.Writers.AnnotatedTable
|
||||
import qualified Tests.Writers.TEI
|
||||
import qualified Tests.Writers.Markua
|
||||
import qualified Tests.Writers.BBCode
|
||||
import qualified Tests.XML
|
||||
import qualified Tests.MediaBag
|
||||
import Text.Pandoc.Shared (inDirectory)
|
||||
@@ -82,6 +83,7 @@ tests pandocPath = testGroup "pandoc tests"
|
||||
, testGroup "PowerPoint" Tests.Writers.Powerpoint.tests
|
||||
, testGroup "Ms" Tests.Writers.Ms.tests
|
||||
, testGroup "AnnotatedTable" Tests.Writers.AnnotatedTable.tests
|
||||
, testGroup "BBCode" Tests.Writers.BBCode.tests
|
||||
]
|
||||
, testGroup "Readers"
|
||||
[ testGroup "LaTeX" Tests.Readers.LaTeX.tests
|
||||
|
||||
@@ -0,0 +1,729 @@
|
||||
This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite.
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Headers[/b][/u]
|
||||
|
||||
[b]Level 2 with an [url=/url]embedded link[/url][/b]
|
||||
|
||||
[u]Level 3 with [i]emphasis[/i][/u]
|
||||
|
||||
Level 4
|
||||
|
||||
Level 5
|
||||
|
||||
[u][b]Level 1[/b][/u]
|
||||
|
||||
[b]Level 2 with [i]emphasis[/i][/b]
|
||||
|
||||
[u]Level 3[/u]
|
||||
|
||||
with no blank line
|
||||
|
||||
[b]Level 2[/b]
|
||||
|
||||
with no blank line
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Paragraphs[/b][/u]
|
||||
|
||||
Here's a regular paragraph.
|
||||
|
||||
In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.
|
||||
|
||||
Here's one with a bullet. * criminey.
|
||||
|
||||
There should be a hard line break
|
||||
here.
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Block Quotes[/b][/u]
|
||||
|
||||
E-mail style:
|
||||
|
||||
[quote]
|
||||
This is a block quote. It is pretty short.
|
||||
[/quote]
|
||||
|
||||
[quote]
|
||||
Code in a block quote:
|
||||
|
||||
[code]sub status {
|
||||
print "working";
|
||||
}
|
||||
[/code]
|
||||
|
||||
A list:
|
||||
|
||||
[list=1]
|
||||
[*]item one
|
||||
[*]item two
|
||||
[/list]
|
||||
|
||||
Nested block quotes:
|
||||
|
||||
[quote]
|
||||
nested
|
||||
[/quote]
|
||||
|
||||
[quote]
|
||||
nested
|
||||
[/quote]
|
||||
[/quote]
|
||||
|
||||
This should not be a block quote: 2 > 1.
|
||||
|
||||
And a following paragraph.
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Code Blocks[/b][/u]
|
||||
|
||||
Code:
|
||||
|
||||
[code]---- (should be four hyphens)
|
||||
|
||||
sub status {
|
||||
print "working";
|
||||
}
|
||||
|
||||
this code block is indented by one tab
|
||||
[/code]
|
||||
|
||||
And:
|
||||
|
||||
[code] this code block is indented by two tabs
|
||||
|
||||
These should not be escaped: \$ \\ \> \[ \{
|
||||
[/code]
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Lists[/b][/u]
|
||||
|
||||
[b]Unordered[/b]
|
||||
|
||||
Asterisks tight:
|
||||
|
||||
[list]
|
||||
[*]asterisk 1
|
||||
[*]asterisk 2
|
||||
[*]asterisk 3
|
||||
[/list]
|
||||
|
||||
Asterisks loose:
|
||||
|
||||
[list]
|
||||
[*]asterisk 1
|
||||
[*]asterisk 2
|
||||
[*]asterisk 3
|
||||
[/list]
|
||||
|
||||
Pluses tight:
|
||||
|
||||
[list]
|
||||
[*]Plus 1
|
||||
[*]Plus 2
|
||||
[*]Plus 3
|
||||
[/list]
|
||||
|
||||
Pluses loose:
|
||||
|
||||
[list]
|
||||
[*]Plus 1
|
||||
[*]Plus 2
|
||||
[*]Plus 3
|
||||
[/list]
|
||||
|
||||
Minuses tight:
|
||||
|
||||
[list]
|
||||
[*]Minus 1
|
||||
[*]Minus 2
|
||||
[*]Minus 3
|
||||
[/list]
|
||||
|
||||
Minuses loose:
|
||||
|
||||
[list]
|
||||
[*]Minus 1
|
||||
[*]Minus 2
|
||||
[*]Minus 3
|
||||
[/list]
|
||||
|
||||
[b]Ordered[/b]
|
||||
|
||||
Tight:
|
||||
|
||||
[list=1]
|
||||
[*]First
|
||||
[*]Second
|
||||
[*]Third
|
||||
[/list]
|
||||
|
||||
and:
|
||||
|
||||
[list=1]
|
||||
[*]One
|
||||
[*]Two
|
||||
[*]Three
|
||||
[/list]
|
||||
|
||||
Loose using tabs:
|
||||
|
||||
[list=1]
|
||||
[*]First
|
||||
[*]Second
|
||||
[*]Third
|
||||
[/list]
|
||||
|
||||
and using spaces:
|
||||
|
||||
[list=1]
|
||||
[*]One
|
||||
[*]Two
|
||||
[*]Three
|
||||
[/list]
|
||||
|
||||
Multiple paragraphs:
|
||||
|
||||
[list=1]
|
||||
[*]Item 1, graf one.
|
||||
|
||||
Item 1. graf two. The quick brown fox jumped over the lazy dog's back.
|
||||
[*]Item 2.
|
||||
[*]Item 3.
|
||||
[/list]
|
||||
|
||||
[b]Nested[/b]
|
||||
|
||||
[list]
|
||||
[*]Tab
|
||||
|
||||
[list]
|
||||
[*]Tab
|
||||
|
||||
[list]
|
||||
[*]Tab
|
||||
[/list]
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
Here's another:
|
||||
|
||||
[list=1]
|
||||
[*]First
|
||||
[*]Second:
|
||||
|
||||
[list]
|
||||
[*]Fee
|
||||
[*]Fie
|
||||
[*]Foe
|
||||
[/list]
|
||||
[*]Third
|
||||
[/list]
|
||||
|
||||
Same thing but with paragraphs:
|
||||
|
||||
[list=1]
|
||||
[*]First
|
||||
[*]Second:
|
||||
|
||||
[list]
|
||||
[*]Fee
|
||||
[*]Fie
|
||||
[*]Foe
|
||||
[/list]
|
||||
[*]Third
|
||||
[/list]
|
||||
|
||||
[b]Tabs and spaces[/b]
|
||||
|
||||
[list]
|
||||
[*]this is a list item indented with tabs
|
||||
[*]this is a list item indented with spaces
|
||||
|
||||
[list]
|
||||
[*]this is an example list item indented with tabs
|
||||
[*]this is an example list item indented with spaces
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
[b]Fancy list markers[/b]
|
||||
|
||||
[list=1]
|
||||
[*]begins with 2
|
||||
[*]and now 3
|
||||
|
||||
with a continuation
|
||||
|
||||
[list=i]
|
||||
[*]sublist with roman numerals, starting with 4
|
||||
[*]more items
|
||||
|
||||
[list=A]
|
||||
[*]a subsublist
|
||||
[*]a subsublist
|
||||
[/list]
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
Nesting:
|
||||
|
||||
[list=A]
|
||||
[*]Upper Alpha
|
||||
|
||||
[list=I]
|
||||
[*]Upper Roman.
|
||||
|
||||
[list=1]
|
||||
[*]Decimal start with 6
|
||||
|
||||
[list=a]
|
||||
[*]Lower alpha with paren
|
||||
[/list]
|
||||
[/list]
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
Autonumbering:
|
||||
|
||||
[list=1]
|
||||
[*]Autonumber.
|
||||
[*]More.
|
||||
|
||||
[list=1]
|
||||
[*]Nested.
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
Should not be a list item:
|
||||
|
||||
M.A. 2007
|
||||
|
||||
B. Williams
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Definition Lists[/b][/u]
|
||||
|
||||
Tight using spaces:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
[/list]
|
||||
banana
|
||||
[list]
|
||||
[*]yellow fruit
|
||||
[/list]
|
||||
|
||||
Tight using tabs:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
[/list]
|
||||
banana
|
||||
[list]
|
||||
[*]yellow fruit
|
||||
[/list]
|
||||
|
||||
Loose:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
[/list]
|
||||
banana
|
||||
[list]
|
||||
[*]yellow fruit
|
||||
[/list]
|
||||
|
||||
Multiple blocks with italics:
|
||||
|
||||
[i]apple[/i]
|
||||
[list]
|
||||
[*]red fruit
|
||||
|
||||
contains seeds, crisp, pleasant to taste
|
||||
[/list]
|
||||
[i]orange[/i]
|
||||
[list]
|
||||
[*]orange fruit
|
||||
|
||||
[code]{ orange code block }
|
||||
[/code]
|
||||
|
||||
[quote]
|
||||
orange block quote
|
||||
[/quote]
|
||||
[/list]
|
||||
|
||||
Multiple definitions, tight:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[*]computer
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
[*]bank
|
||||
[/list]
|
||||
|
||||
Multiple definitions, loose:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[*]computer
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
[*]bank
|
||||
[/list]
|
||||
|
||||
Blank line after term, indented marker, alternate markers:
|
||||
|
||||
apple
|
||||
[list]
|
||||
[*]red fruit
|
||||
[*]computer
|
||||
[/list]
|
||||
orange
|
||||
[list]
|
||||
[*]orange fruit
|
||||
|
||||
[list=1]
|
||||
[*]sublist
|
||||
[*]sublist
|
||||
[/list]
|
||||
[/list]
|
||||
|
||||
[u][b]HTML Blocks[/b][/u]
|
||||
|
||||
Simple block on one line:
|
||||
|
||||
foo
|
||||
|
||||
And nested without indentation:
|
||||
|
||||
foo
|
||||
|
||||
bar
|
||||
|
||||
Interpreted markdown in a table:
|
||||
|
||||
This is [i]emphasized[/i]
|
||||
|
||||
And this is [b]strong[/b]
|
||||
|
||||
Here's a simple block:
|
||||
|
||||
foo
|
||||
|
||||
This should be a code block, though:
|
||||
|
||||
[code]<div>
|
||||
foo
|
||||
</div>
|
||||
[/code]
|
||||
|
||||
As should this:
|
||||
|
||||
[code]<div>foo</div>
|
||||
[/code]
|
||||
|
||||
Now, nested:
|
||||
|
||||
foo
|
||||
|
||||
This should just be an HTML comment:
|
||||
|
||||
Multiline:
|
||||
|
||||
Code block:
|
||||
|
||||
[code]<!-- Comment -->
|
||||
[/code]
|
||||
|
||||
Just plain comment, with trailing spaces on the line:
|
||||
|
||||
Code:
|
||||
|
||||
[code]<hr />
|
||||
[/code]
|
||||
|
||||
Hr's:
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Inline Markup[/b][/u]
|
||||
|
||||
This is [i]emphasized[/i], and so [i]is this[/i].
|
||||
|
||||
This is [b]strong[/b], and so [b]is this[/b].
|
||||
|
||||
An [i][url=/url]emphasized link[/url][/i].
|
||||
|
||||
[b][i]This is strong and em.[/i][/b]
|
||||
|
||||
So is [b][i]this[/i][/b] word.
|
||||
|
||||
[b][i]This is strong and em.[/i][/b]
|
||||
|
||||
So is [b][i]this[/i][/b] word.
|
||||
|
||||
This is code: >, $, \, \$, <html>.
|
||||
|
||||
[s]This is [i]strikeout[/i].[/s]
|
||||
|
||||
Superscripts: abcd a[i]hello[/i] ahello there.
|
||||
|
||||
Subscripts: H2O, H23O, Hmany of themO.
|
||||
|
||||
These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Smart quotes, ellipses, dashes[/b][/u]
|
||||
|
||||
"Hello," said the spider. "'Shelob' is my name."
|
||||
|
||||
'A', 'B', and 'C' are letters.
|
||||
|
||||
'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
|
||||
|
||||
'He said, "I want to go."' Were you alive in the 70's?
|
||||
|
||||
Here is some quoted 'code' and a "[url=http://example.com/?foo=1&bar=2]quoted link[/url]".
|
||||
|
||||
Some dashes: one---two --- three---four --- five.
|
||||
|
||||
Dashes between numbers: 5--7, 255--66, 1987--1999.
|
||||
|
||||
Ellipses...and...and....
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]LaTeX[/b][/u]
|
||||
|
||||
[list]
|
||||
[*]
|
||||
[*]$2+2=4$
|
||||
[*]$x \in y$
|
||||
[*]$\alpha \wedge \omega$
|
||||
[*]$223$
|
||||
[*]$p$-Tree
|
||||
[*]Here's some display math: [code=latex]$$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
|
||||
[/code]
|
||||
[*]Here's one that has a line break in it: $\alpha + \omega \times x^2$.
|
||||
[/list]
|
||||
|
||||
These shouldn't be math:
|
||||
|
||||
[list]
|
||||
[*]To get the famous equation, write $e = mc^2$.
|
||||
[*]$22,000 is a [i]lot[/i] of money. So is $34,000. (It worked if "lot" is emphasized.)
|
||||
[*]Shoes ($20) and socks ($5).
|
||||
[*]Escaped $: $73 [i]this should be emphasized[/i] 23$.
|
||||
[/list]
|
||||
|
||||
Here's a LaTeX table:
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Special Characters[/b][/u]
|
||||
|
||||
Here is some unicode:
|
||||
|
||||
[list]
|
||||
[*]I hat: Î
|
||||
[*]o umlaut: ö
|
||||
[*]section: §
|
||||
[*]set membership: ∈
|
||||
[*]copyright: ©
|
||||
[/list]
|
||||
|
||||
AT&T has an ampersand in their name.
|
||||
|
||||
AT&T is another way to write it.
|
||||
|
||||
This & that.
|
||||
|
||||
4 < 5.
|
||||
|
||||
6 > 5.
|
||||
|
||||
Backslash: \
|
||||
|
||||
Backtick: `
|
||||
|
||||
Asterisk: *
|
||||
|
||||
Underscore: _
|
||||
|
||||
Left brace: {
|
||||
|
||||
Right brace: }
|
||||
|
||||
Left bracket: [
|
||||
|
||||
Right bracket: ]
|
||||
|
||||
Left paren: (
|
||||
|
||||
Right paren: )
|
||||
|
||||
Greater-than: >
|
||||
|
||||
Hash: #
|
||||
|
||||
Period: .
|
||||
|
||||
Bang: !
|
||||
|
||||
Plus: +
|
||||
|
||||
Minus: -
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Links[/b][/u]
|
||||
|
||||
[b]Explicit[/b]
|
||||
|
||||
Just a [url=/url/]URL[/url].
|
||||
|
||||
[url=/url/]URL and title[/url].
|
||||
|
||||
[url=/url/]URL and title[/url].
|
||||
|
||||
[url=/url/]URL and title[/url].
|
||||
|
||||
[url=/url/]URL and title[/url]
|
||||
|
||||
[url=/url/]URL and title[/url]
|
||||
|
||||
[url=/url/with_underscore]with_underscore[/url]
|
||||
|
||||
[email=nobody@nowhere.net]Email link[/email]
|
||||
|
||||
[url]Empty[/url].
|
||||
|
||||
[b]Reference[/b]
|
||||
|
||||
Foo [url=/url/]bar[/url].
|
||||
|
||||
With [url=/url/]embedded [brackets][/url].
|
||||
|
||||
[url=/url/]b[/url] by itself should be a link.
|
||||
|
||||
Indented [url=/url]once[/url].
|
||||
|
||||
Indented [url=/url]twice[/url].
|
||||
|
||||
Indented [url=/url]thrice[/url].
|
||||
|
||||
This should [not][] be a link.
|
||||
|
||||
[code][not]: /url
|
||||
[/code]
|
||||
|
||||
Foo [url=/url/]bar[/url].
|
||||
|
||||
Foo [url=/url/]biz[/url].
|
||||
|
||||
[b]With ampersands[/b]
|
||||
|
||||
Here's a [url=http://example.com/?foo=1&bar=2]link with an ampersand in the URL[/url].
|
||||
|
||||
Here's a link with an amersand in the link text: [url=http://att.com/]AT&T[/url].
|
||||
|
||||
Here's an [url=/script?foo=1&bar=2]inline link[/url].
|
||||
|
||||
Here's an [url=/script?foo=1&bar=2]inline link in pointy braces[/url].
|
||||
|
||||
[b]Autolinks[/b]
|
||||
|
||||
With an ampersand: [url]http://example.com/?foo=1&bar=2[/url]
|
||||
|
||||
[list]
|
||||
[*]In a list?
|
||||
[*][url]http://example.com/[/url]
|
||||
[*]It should.
|
||||
[/list]
|
||||
|
||||
An e-mail address: [email]nobody@nowhere.net[/email]
|
||||
|
||||
[quote]
|
||||
Blockquoted: [url]http://example.com/[/url]
|
||||
[/quote]
|
||||
|
||||
Auto-links should not occur here: <http://example.com/>
|
||||
|
||||
[code]or here: <http://example.com/>
|
||||
[/code]
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Images[/b][/u]
|
||||
|
||||
From "Voyage dans la Lune" by Georges Melies (1902):
|
||||
|
||||
[img alt="lalune" title="Voyage dans la Lune"]lalune.jpg[/img]
|
||||
lalune
|
||||
|
||||
Here is a movie [img alt="movie"]movie.jpg[/img] icon.
|
||||
|
||||
* * *
|
||||
|
||||
[u][b]Footnotes[/b][/u]
|
||||
|
||||
Here is a footnote reference,(1) and another.(2) This should [i]not[/i] be a footnote reference, because it contains a space.[^my note] Here is an inline note.(3)
|
||||
|
||||
[quote]
|
||||
Notes can go in quotes.(4)
|
||||
[/quote]
|
||||
|
||||
[list=1]
|
||||
[*]And in list items.(5)
|
||||
[/list]
|
||||
|
||||
This paragraph should not be part of the note, as it is not indented.
|
||||
|
||||
* * *
|
||||
|
||||
(1) Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.
|
||||
|
||||
(2) Here's the long note. This one contains multiple blocks.
|
||||
|
||||
Subsequent blocks are indented to show that they belong to the footnote (as with list items).
|
||||
|
||||
[code] { <code> }
|
||||
[/code]
|
||||
|
||||
If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
|
||||
|
||||
(3) This is [i]easier[/i] to type. Inline notes may contain [url=http://google.com]links[/url] and ] verbatim characters, as well as [bracketed text].
|
||||
|
||||
(4) In quote.
|
||||
|
||||
(5) In list.
|
||||
Reference in New Issue
Block a user