pandoc/test/Tests/Readers/Muse.hs
2017-12-30 20:32:16 +03:00

983 lines
35 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
module Tests.Readers.Muse (tests) where
import Data.List (intersperse)
import Data.Text (Text)
import qualified Data.Text as T
import Test.Tasty
import Test.Tasty.QuickCheck
import Tests.Helpers
import Text.Pandoc
import Text.Pandoc.Arbitrary ()
import Text.Pandoc.Builder
import Text.Pandoc.Shared (underlineSpan)
import Text.Pandoc.Walk (walk)
amuse :: Text -> Pandoc
amuse = purely $ readMuse def { readerExtensions = extensionsFromList [Ext_amuse]}
emacsMuse :: Text -> Pandoc
emacsMuse = purely $ readMuse def { readerExtensions = emptyExtensions }
infix 4 =:
(=:) :: ToString c
=> String -> (Text, c) -> TestTree
(=:) = test amuse
spcSep :: [Inlines] -> Inlines
spcSep = mconcat . intersperse space
-- Tables and definition lists don't round-trip yet
makeRoundTrip :: Block -> Block
makeRoundTrip (Table{}) = Para [Str "table was here"]
makeRoundTrip (DefinitionList{}) = Para [Str "deflist was here"]
makeRoundTrip x = x
-- Demand that any AST produced by Muse reader and written by Muse writer can be read back exactly the same way.
-- Currently we remove code blocks and tables and compare third rewrite to the second.
-- First and second rewrites are not equal yet.
roundTrip :: Block -> Bool
roundTrip b = d'' == d'''
where d = walk makeRoundTrip $ Pandoc nullMeta [b]
d' = rewrite d
d'' = rewrite d'
d''' = rewrite d''
rewrite = amuse . T.pack . (++ "\n") . T.unpack .
(purely $ writeMuse def { writerExtensions = extensionsFromList [Ext_amuse]
, writerWrapText = WrapPreserve
})
tests :: [TestTree]
tests =
[ testGroup "Inlines"
[ "Plain String" =:
"Hello, World" =?>
para "Hello, World"
, "Muse is not XML" =: "<" =?> para "<"
, "Emphasis" =:
"*Foo bar*" =?>
para (emph . spcSep $ ["Foo", "bar"])
, "Comma after closing *" =:
"Foo *bar*, baz" =?>
para ("Foo " <> emph "bar" <> ", baz")
, "Letter after closing *" =:
"Foo *bar*x baz" =?>
para "Foo *bar*x baz"
, "Letter before opening *" =:
"Foo x*bar* baz" =?>
para "Foo x*bar* baz"
, "Emphasis tag" =:
"<em>Foo bar</em>" =?>
para (emph . spcSep $ ["Foo", "bar"])
, "Strong" =:
"**Cider**" =?>
para (strong "Cider")
, "Strong tag" =: "<strong>Strong</strong>" =?> para (strong "Strong")
, "Strong Emphasis" =:
"***strength***" =?>
para (strong . emph $ "strength")
, test emacsMuse "Underline"
("_Underline_" =?> para (underlineSpan "Underline"))
, "Superscript tag" =: "<sup>Superscript</sup>" =?> para (superscript "Superscript")
, "Subscript tag" =: "<sub>Subscript</sub>" =?> para (subscript "Subscript")
, "Strikeout tag" =: "<del>Strikeout</del>" =?> para (strikeout "Strikeout")
, "Opening inline tags" =: "foo <em> bar <strong>baz" =?> para "foo <em> bar <strong>baz"
, "Closing inline tags" =: "foo </em> bar </strong>baz" =?> para "foo </em> bar </strong>baz"
, "Tag soup" =: "foo <em> bar </strong>baz" =?> para "foo <em> bar </strong>baz"
-- Both inline tags must be within the same paragraph
, "No multiparagraph inline tags" =:
T.unlines [ "First line"
, "<em>Second line"
, ""
, "Fourth line</em>"
] =?>
para "First line\n<em>Second line" <>
para "Fourth line</em>"
, "Linebreak" =: "Line <br> break" =?> para ("Line" <> linebreak <> "break")
, test emacsMuse "Non-breaking space"
("Foo~~bar" =?> para "Foo\160bar")
, testGroup "Code markup"
[ "Code" =: "=foo(bar)=" =?> para (code "foo(bar)")
, "Not code" =: "a=b= =c=d" =?> para (text "a=b= =c=d")
-- Emacs Muse 3.20 parses this as code, we follow Amusewiki
, "Not code if closing = is detached" =: "=this is not a code =" =?> para "=this is not a code ="
, "Not code if opening = is detached" =: "= this is not a code=" =?> para "= this is not a code="
, "Code if followed by comma" =:
"Foo =bar=, baz" =?>
para (text "Foo " <> code "bar" <> text ", baz")
, "One character code" =: "=c=" =?> para (code "c")
, "Three = characters is not a code" =: "===" =?> para "==="
, "Multiline code markup" =:
"foo =bar\nbaz= end of code" =?>
para (text "foo " <> code "bar\nbaz" <> text " end of code")
{- Emacs Muse 3.20 has a bug: it publishes
- <p>foo <code>bar
-
- baz</code> foo</p>
- which is displayed as one paragraph by browsers.
- We follow Amusewiki here and avoid joining paragraphs.
-}
, "No multiparagraph code" =:
T.unlines [ "foo =bar"
, ""
, "baz= foo"
] =?>
para "foo =bar" <>
para "baz= foo"
]
, "Code tag" =: "<code>foo(bar)</code>" =?> para (code "foo(bar)")
, "Verbatim tag" =: "*<verbatim>*</verbatim>*" =?> para (emph "*")
, "Verbatim inside code" =: "<code><verbatim>foo</verbatim></code>" =?> para (code "<verbatim>foo</verbatim>")
, testGroup "Links"
[ "Link without description" =:
"[[https://amusewiki.org/]]" =?>
para (link "https://amusewiki.org/" "" (str "https://amusewiki.org/"))
, "Link with description" =:
"[[https://amusewiki.org/][A Muse Wiki]]" =?>
para (link "https://amusewiki.org/" "" (text "A Muse Wiki"))
, "Image" =:
"[[image.jpg]]" =?>
para (image "image.jpg" "" mempty)
, "Image with description" =:
"[[image.jpg][Image]]" =?>
para (image "image.jpg" "" (text "Image"))
, "Image link" =:
"[[URL:image.jpg]]" =?>
para (link "image.jpg" "" (str "image.jpg"))
, "Image link with description" =:
"[[URL:image.jpg][Image]]" =?>
para (link "image.jpg" "" (text "Image"))
-- Implicit links are supported in Emacs Muse, but not in Amusewiki:
-- https://github.com/melmothx/text-amuse/issues/18
--
-- This test also makes sure '=' without whitespace is not treated as code markup
, "No implicit links" =: "http://example.org/index.php?action=view&id=1"
=?> para "http://example.org/index.php?action=view&id=1"
]
, testGroup "Literal"
[ test emacsMuse "Inline literal"
("Foo<literal style=\"html\">lit</literal>bar" =?>
para (text "Foo" <> rawInline "html" "lit" <> text "bar"))
, "No literal in Text::Amuse" =:
"Foo<literal style=\"html\">lit</literal>bar" =?>
para "Foo<literal style=\"html\">lit</literal>bar"
]
]
, testGroup "Blocks"
[ testProperty "Round trip" roundTrip,
"Block elements end paragraphs" =:
T.unlines [ "First paragraph"
, "----"
, "Second paragraph"
] =?> para (text "First paragraph") <> horizontalRule <> para (text "Second paragraph")
, testGroup "Horizontal rule"
[ "Less than 4 dashes is not a horizontal rule" =: "---" =?> para (text "---")
, "4 dashes is a horizontal rule" =: "----" =?> horizontalRule
, "5 dashes is a horizontal rule" =: "-----" =?> horizontalRule
, "4 dashes with spaces is a horizontal rule" =: "---- " =?> horizontalRule
]
, testGroup "Paragraphs"
[ "Simple paragraph" =:
T.unlines [ "First line"
, "second line."
] =?>
para "First line\nsecond line."
, "Indented paragraph" =:
T.unlines [ " First line"
, "second line."
] =?>
para "First line\nsecond line."
-- Emacs Muse starts a blockquote on the second line.
-- We copy Amusewiki behavior and require a blank line to start a blockquote.
, "Indentation in the middle of paragraph" =:
T.unlines [ "First line"
, " second line"
, "third line"
] =?>
para "First line\nsecond line\nthird line"
, "Quote" =:
" This is a quotation\n" =?>
blockQuote (para "This is a quotation")
, "Multiline quote" =:
T.unlines [ " This is a quotation"
, " with a continuation"
] =?>
blockQuote (para "This is a quotation\nwith a continuation")
, testGroup "Div"
[ "Div without id" =:
"<div>Foo bar</div>" =?>
divWith nullAttr (para "Foo bar")
, "Div with id" =:
"<div id=\"foo\">Foo bar</div>" =?>
divWith ("foo", [], []) (para "Foo bar")
]
, "Verse" =:
T.unlines [ "> This is"
, "> First stanza"
, ">" -- Emacs produces verbatim ">" here, we follow Amusewiki
, "> And this is"
, "> Second stanza"
, ">"
, ""
, ">"
, ""
, "> Another verse"
, "> is here"
] =?>
lineBlock [ "This is"
, "First stanza"
, ""
, "And this is"
, "\160\160Second stanza"
, ""
] <>
lineBlock [ "" ] <>
lineBlock [ "Another verse"
, "\160\160\160is here"
]
]
, "Quote tag" =: "<quote>Hello, world</quote>" =?> blockQuote (para $ text "Hello, world")
, "Verse tag" =:
T.unlines [ "<verse>"
, ""
, "Foo bar baz"
, " One two three"
, ""
, "</verse>"
, "<verse>Foo bar</verse>"
, "<verse>"
, "Foo bar</verse>"
, "<verse>"
, " Foo</verse>"
] =?>
lineBlock [ ""
, text "Foo bar baz"
, text "\160\160One two three"
, ""
] <>
lineBlock [ "Foo bar" ] <>
lineBlock [ "Foo bar" ] <>
lineBlock [ "\160\160\160Foo" ]
, testGroup "Example"
[ "Braces on separate lines" =:
T.unlines [ "{{{"
, "Example line"
, "}}}"
] =?>
codeBlock "Example line"
, "Spaces after opening braces" =:
T.unlines [ "{{{ "
, "Example line"
, "}}}"
] =?>
codeBlock "Example line"
, "One blank line in the beginning" =:
T.unlines [ "{{{"
, ""
, "Example line"
, "}}}"
] =?>
codeBlock "\nExample line"
, "One blank line in the end" =:
T.unlines [ "{{{"
, "Example line"
, ""
, "}}}"
] =?>
codeBlock "Example line\n"
-- Amusewiki requires braces to be on separate line,
-- this is an extension.
, "One line" =:
"{{{Example line}}}" =?>
codeBlock "Example line"
]
, testGroup "Example tag"
[ "Tags on separate lines" =:
T.unlines [ "<example>"
, "Example line"
, "</example>"
] =?>
codeBlock "Example line"
, "One line" =:
"<example>Example line</example>" =?>
codeBlock "Example line"
, "One blank line in the beginning" =:
T.unlines [ "<example>"
, ""
, "Example line"
, "</example>"
] =?>
codeBlock "\nExample line"
, "One blank line in the end" =:
T.unlines [ "<example>"
, "Example line"
, ""
, "</example>"
] =?>
codeBlock "Example line\n"
, "Example inside list" =:
T.unlines [ " - <example>"
, " foo"
, " </example>"
] =?>
bulletList [ codeBlock "foo" ]
, "Example inside list with empty lines" =:
T.unlines [ " - <example>"
, " foo"
, " </example>"
, ""
, " bar"
, ""
, " <example>"
, " baz"
, " </example>"
] =?>
bulletList [ codeBlock "foo" <> para "bar" <> codeBlock "baz" ]
, "Indented example inside list" =:
T.unlines [ " - <example>"
, " foo"
, " </example>"
] =?>
bulletList [ codeBlock "foo" ]
, "Example inside definition list" =:
T.unlines [ " foo :: <example>"
, " bar"
, " </example>"
] =?>
definitionList [ ("foo", [codeBlock "bar"]) ]
, "Example inside list definition with empty lines" =:
T.unlines [ " term :: <example>"
, " foo"
, " </example>"
, ""
, " bar"
, ""
, " <example>"
, " baz"
, " </example>"
] =?>
definitionList [ ("term", [codeBlock "foo" <> para "bar" <> codeBlock "baz"]) ]
, "Example inside note" =:
T.unlines [ "Foo[1]"
, ""
, "[1] <example>"
, " bar"
, " </example>"
] =?>
para ("Foo" <> note (codeBlock "bar"))
]
, testGroup "Literal blocks"
[ test emacsMuse "Literal block"
(T.unlines [ "<literal style=\"latex\">"
, "\\newpage"
, "</literal>"
] =?>
rawBlock "latex" "\\newpage")
, "No literal blocks in Text::Amuse" =:
T.unlines [ "<literal style=\"latex\">"
, "\\newpage"
, "</literal>"
] =?>
para "<literal style=\"latex\">\n\\newpage\n</literal>"
]
, "Center" =: "<center>Hello, world</center>" =?> para (text "Hello, world")
, "Right" =: "<right>Hello, world</right>" =?> para (text "Hello, world")
, testGroup "Comments"
[ "Comment tag" =: "<comment>\nThis is a comment\n</comment>" =?> (mempty::Blocks)
, "Line comment" =: "; Comment" =?> (mempty::Blocks)
, "Empty comment" =: ";" =?> (mempty::Blocks)
, "Text after empty comment" =: ";\nfoo" =?> para "foo" -- Make sure we don't consume newline while looking for whitespace
, "Not a comment (does not start with a semicolon)" =: " ; Not a comment" =?> para (text "; Not a comment")
, "Not a comment (has no space after semicolon)" =: ";Not a comment" =?> para (text ";Not a comment")
]
, testGroup "Headers"
[ "Part" =:
"* First level" =?>
header 1 "First level"
, "Chapter" =:
"** Second level" =?>
header 2 "Second level"
, "Section" =:
"*** Third level" =?>
header 3 "Third level"
, "Subsection" =:
"**** Fourth level" =?>
header 4 "Fourth level"
, "Subsubsection" =:
"***** Fifth level" =?>
header 5 "Fifth level"
, "Whitespace is required after *" =: "**Not a header" =?> para "**Not a header"
, "No headers in footnotes" =:
T.unlines [ "Foo[1]"
, "[1] * Bar"
] =?>
para (text "Foo" <>
note (para "* Bar"))
, "No headers in quotes" =:
T.unlines [ "<quote>"
, "* Hi"
, "</quote>"
] =?>
blockQuote (para "* Hi")
, "Headers consume anchors" =:
T.unlines [ "** Foo"
, "#bar"
] =?>
headerWith ("bar",[],[]) 2 "Foo"
, "Headers don't consume anchors separated with a blankline" =:
T.unlines [ "** Foo"
, ""
, "#bar"
] =?>
header 2 "Foo" <>
para (spanWith ("bar", [], []) mempty)
]
, testGroup "Directives"
[ "Title" =:
"#title Document title" =?>
let titleInline = toList "Document title"
meta = setMeta "title" (MetaInlines titleInline) nullMeta
in Pandoc meta mempty
-- Emacs Muse documentation says that "You can use any combination
-- of uppercase and lowercase letters for directives",
-- but also allows '-', which is not documented, but used for disable-tables.
, test emacsMuse "Disable tables"
("#disable-tables t" =?>
Pandoc (setMeta "disable-tables" (MetaInlines $ toList "t") nullMeta) mempty)
]
, testGroup "Anchors"
[ "Anchor" =:
T.unlines [ "; A comment to make sure anchor is not parsed as a directive"
, "#anchor Target"
] =?>
para (spanWith ("anchor", [], []) mempty <> "Target")
, "Anchor cannot start with a number" =:
T.unlines [ "; A comment to make sure anchor is not parsed as a directive"
, "#0notanchor Target"
] =?>
para "#0notanchor Target"
, "Not anchor if starts with a space" =:
" #notanchor Target" =?>
para "#notanchor Target"
, "Anchor inside a paragraph" =:
T.unlines [ "Paragraph starts here"
, "#anchor and ends here."
] =?>
para ("Paragraph starts here\n" <> spanWith ("anchor", [], []) mempty <> "and ends here.")
]
, testGroup "Footnotes"
[ "Simple footnote" =:
T.unlines [ "Here is a footnote[1]."
, ""
, "[1] Footnote contents"
] =?>
para (text "Here is a footnote" <>
note (para "Footnote contents") <>
str ".")
, "Recursive footnote" =:
T.unlines [ "Start recursion here[1]"
, ""
, "[1] Recursion continues here[1]"
] =?>
para (text "Start recursion here" <>
note (para "Recursion continues here[1]"))
, "No zero footnotes" =:
T.unlines [ "Here is a footnote[0]."
, ""
, "[0] Footnote contents"
] =?>
para "Here is a footnote[0]." <>
para "[0] Footnote contents"
, "Footnotes can't start with zero" =:
T.unlines [ "Here is a footnote[01]."
, ""
, "[01] Footnote contents"
] =?>
para "Here is a footnote[01]." <>
para "[01] Footnote contents"
, testGroup "Multiparagraph footnotes"
[ "Amusewiki multiparagraph footnotes" =:
T.unlines [ "Multiparagraph[1] footnotes[2]"
, ""
, "[1] First footnote paragraph"
, ""
, " Second footnote paragraph"
, "Not a note"
, "[2] Second footnote"
] =?>
para (text "Multiparagraph" <>
note (para "First footnote paragraph" <>
para "Second footnote paragraph") <>
text " footnotes" <>
note (para "Second footnote")) <>
para (text "Not a note")
, test emacsMuse "Emacs multiparagraph footnotes"
(T.unlines
[ "First footnote reference[1] and second footnote reference[2]."
, ""
, "[1] First footnote paragraph"
, ""
, "Second footnote"
, "paragraph"
, ""
, "[2] Third footnote paragraph"
, ""
, "Fourth footnote paragraph"
] =?>
para (text "First footnote reference" <>
note (para "First footnote paragraph" <>
para "Second footnote\nparagraph") <>
text " and second footnote reference" <>
note (para "Third footnote paragraph" <>
para "Fourth footnote paragraph") <>
text "."))
]
]
]
, testGroup "Tables"
[ "Two cell table" =:
"One | Two" =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[]
[[plain "One", plain "Two"]]
, "Table with multiple words" =:
"One two | three four" =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[]
[[plain "One two", plain "three four"]]
, "Not a table" =:
"One| Two" =?>
para (text "One| Two")
, "Not a table again" =:
"One |Two" =?>
para (text "One |Two")
, "Two line table" =:
T.unlines
[ "One | Two"
, "Three | Four"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[]
[[plain "One", plain "Two"],
[plain "Three", plain "Four"]]
, "Table with one header" =:
T.unlines
[ "First || Second"
, "Third | Fourth"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[plain "First", plain "Second"]
[[plain "Third", plain "Fourth"]]
, "Table with two headers" =:
T.unlines
[ "First || header"
, "Second || header"
, "Foo | bar"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[plain "First", plain "header"]
[[plain "Second", plain "header"],
[plain "Foo", plain "bar"]]
, "Header and footer reordering" =:
T.unlines
[ "Foo ||| bar"
, "Baz || foo"
, "Bar | baz"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[plain "Baz", plain "foo"]
[[plain "Bar", plain "baz"],
[plain "Foo", plain "bar"]]
, "Table with caption" =:
T.unlines
[ "Foo || bar || baz"
, "First | row | here"
, "Second | row | there"
, "|+ Table caption +|"
] =?>
table (text "Table caption") (replicate 3 (AlignDefault, 0.0))
[plain "Foo", plain "bar", plain "baz"]
[[plain "First", plain "row", plain "here"],
[plain "Second", plain "row", plain "there"]]
, "Caption without table" =:
"|+ Foo bar baz +|" =?>
table (text "Foo bar baz") [] [] []
, "Table indented with space" =:
T.unlines
[ " Foo | bar"
, " Baz | foo"
, " Bar | baz"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[]
[[plain "Foo", plain "bar"],
[plain "Baz", plain "foo"],
[plain "Bar", plain "baz"]]
, "Empty cells" =:
T.unlines
[ " | Foo"
, " |"
, " bar |"
, " || baz"
] =?>
table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
[plain "", plain "baz"]
[[plain "", plain "Foo"],
[plain "", plain ""],
[plain "bar", plain ""]]
]
, testGroup "Lists"
[ "Bullet list" =:
T.unlines
[ " - Item1"
, ""
, " - Item2"
] =?>
bulletList [ para "Item1"
, para "Item2"
]
, "Ordered list" =:
T.unlines
[ " 1. Item1"
, ""
, " 2. Item2"
] =?>
orderedListWith (1, Decimal, Period) [ para "Item1"
, para "Item2"
]
, "Ordered list with implicit numbers" =:
T.unlines
[ " 1. Item1"
, ""
, " 1. Item2"
, ""
, " 1. Item3"
] =?>
orderedListWith (1, Decimal, Period) [ para "Item1"
, para "Item2"
, para "Item3"
]
, "Bullet list with empty items" =:
T.unlines
[ " -"
, ""
, " - Item2"
] =?>
bulletList [ mempty
, para "Item2"
]
, "Ordered list with empty items" =:
T.unlines
[ " 1."
, ""
, " 2."
, ""
, " 3. Item3"
] =?>
orderedListWith (1, Decimal, Period) [ mempty
, mempty
, para "Item3"
]
, testGroup "Nested lists"
[ "Nested list" =:
T.unlines
[ " - Item1"
, " - Item2"
, " - Item3"
, " - Item4"
, " 1. Nested"
, " 2. Ordered"
, " 3. List"
] =?>
bulletList [ mconcat [ para "Item1"
, bulletList [ para "Item2"
, para "Item3"
]
]
, mconcat [ para "Item4"
, orderedListWith (1, Decimal, Period) [ para "Nested"
, para "Ordered"
, para "List"
]
]
]
, "Incorrectly indented Text::Amuse nested list" =:
T.unlines
[ " - First item"
, " - Not nested item"
] =?>
bulletList [ para "First item", para "Not nested item"]
, "Text::Amuse includes only one space in list marker" =:
T.unlines
[ " - First item"
, " - Nested item"
] =?>
bulletList [ para "First item" <> bulletList [ para "Nested item"]]
]
, "List continuation" =:
T.unlines
[ " - a"
, ""
, " b"
, ""
, " c"
] =?>
bulletList [ mconcat [ para "a"
, para "b"
, para "c"
]
]
-- Emacs Muse allows to separate lists with two or more blank lines.
-- Text::Amuse (Amusewiki engine) always creates a single list as of version 0.82.
-- pandoc follows Emacs Muse behavior
, testGroup "Blank lines"
[ "Blank lines between list items are not required" =:
T.unlines
[ " - Foo"
, " - Bar"
] =?>
bulletList [ para "Foo"
, para "Bar"
]
, "One blank line between list items is allowed" =:
T.unlines
[ " - Foo"
, ""
, " - Bar"
] =?>
bulletList [ para "Foo"
, para "Bar"
]
, "Two blank lines separate lists" =:
T.unlines
[ " - Foo"
, ""
, ""
, " - Bar"
] =?>
bulletList [ para "Foo" ] <> bulletList [ para "Bar" ]
, "No blank line after multiline first item" =:
T.unlines
[ " - Foo"
, " bar"
, " - Baz"
] =?>
bulletList [ para "Foo\nbar"
, para "Baz"
]
, "One blank line after multiline first item" =:
T.unlines
[ " - Foo"
, " bar"
, ""
, " - Baz"
] =?>
bulletList [ para "Foo\nbar"
, para "Baz"
]
, "Two blank lines after multiline first item" =:
T.unlines
[ " - Foo"
, " bar"
, ""
, ""
, " - Baz"
] =?>
bulletList [ para "Foo\nbar" ] <> bulletList [ para "Baz" ]
, "No blank line after list continuation" =:
T.unlines
[ " - Foo"
, ""
, " bar"
, " - Baz"
] =?>
bulletList [ para "Foo" <> para "bar"
, para "Baz"
]
, "One blank line after list continuation" =:
T.unlines
[ " - Foo"
, ""
, " bar"
, ""
, " - Baz"
] =?>
bulletList [ para "Foo" <> para "bar"
, para "Baz"
]
, "Two blank lines after list continuation" =:
T.unlines
[ " - Foo"
, ""
, " bar"
, ""
, ""
, " - Baz"
] =?>
bulletList [ para "Foo" <> para "bar" ] <> bulletList [ para "Baz" ]
]
-- Test that definition list requires a leading space.
-- Emacs Muse does not require a space, we follow Amusewiki here.
, "Not a definition list" =:
T.unlines
[ "First :: second"
, "Foo :: bar"
] =?>
para "First :: second\nFoo :: bar"
, test emacsMuse "Emacs Muse definition list"
(T.unlines
[ "First :: second"
, "Foo :: bar"
] =?>
definitionList [ ("First", [ para "second" ])
, ("Foo", [ para "bar" ])
])
, "Definition list" =:
T.unlines
[ " First :: second"
, " Foo :: bar"
] =?>
definitionList [ ("First", [ para "second" ])
, ("Foo", [ para "bar" ])
]
, "Definition list term cannot include newline" =:
T.unlines
[ " Foo" -- "Foo" is not a part of the definition list term
, " Bar :: baz"
] =?>
para "Foo" <>
definitionList [ ("Bar", [ para "baz" ]) ]
, "One-line definition list" =: " foo :: bar" =?>
definitionList [ ("foo", [ para "bar" ]) ]
, "Definition list term with emphasis" =: " *Foo* :: bar\n" =?>
definitionList [ (emph "Foo", [ para "bar" ]) ]
, "Multi-line definition lists" =:
T.unlines
[ " First term :: Definition of first term"
, "and its continuation."
, " Second term :: Definition of second term."
] =?>
definitionList [ ("First term", [ para "Definition of first term\nand its continuation." ])
, ("Second term", [ para "Definition of second term." ])
]
, test emacsMuse "Multi-line definition lists from Emacs Muse manual"
(T.unlines
[ "Term1 ::"
, " This is a first definition"
, " And it has two lines;"
, "no, make that three."
, ""
, "Term2 :: This is a second definition"
] =?>
definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."])
, ("Term2", [ para "This is a second definition"])
])
-- Text::Amuse requires indentation with one space
, "Multi-line definition lists from Emacs Muse manual with initial space" =:
(T.unlines
[ " Term1 ::"
, " This is a first definition"
, " And it has two lines;"
, "no, make that three."
, ""
, " Term2 :: This is a second definition"
] =?>
definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."])
, ("Term2", [ para "This is a second definition"])
])
-- Emacs Muse creates two separate lists when indentation of items is different.
-- We follow Amusewiki and allow different indentation within one list.
, "Changing indentation" =:
T.unlines
[ " First term :: Definition of first term"
, "and its continuation."
, " Second term :: Definition of second term."
] =?>
definitionList [ ("First term", [ para "Definition of first term\nand its continuation." ])
, ("Second term", [ para "Definition of second term." ])
]
, "Two blank lines separate definition lists" =:
T.unlines
[ " First :: list"
, ""
, ""
, " Second :: list"
] =?>
definitionList [ ("First", [ para "list" ]) ] <>
definitionList [ ("Second", [ para "list" ]) ]
-- Headers in first column of list continuation are not allowed
, "No headers in list continuation" =:
T.unlines
[ " - Foo"
, ""
, " * Bar"
] =?>
bulletList [ mconcat [ para "Foo"
, para "* Bar"
]
]
, "List inside a tag" =:
T.unlines
[ "<quote>"
, " 1. First"
, ""
, " 2. Second"
, ""
, " 3. Third"
, "</quote>"
] =?>
blockQuote (orderedListWith (1, Decimal, Period) [ para "First"
, para "Second"
, para "Third"
])
-- Amusewiki requires block tags to be on separate lines,
-- but Emacs Muse allows them to be on the same line as contents.
, "List inside an inline tag" =:
T.unlines
[ "<quote> 1. First"
, ""
, " 2. Second"
, ""
, " 3. Third</quote>"
] =?>
blockQuote (orderedListWith (1, Decimal, Period) [ para "First"
, para "Second"
, para "Third"
])
]
]