{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Text.Pandoc.XML.Light.Output
(
ppTopElement
, ppElement
, ppContent
, ppcTopElement
, ppcElement
, ppcContent
, showTopElement
, showElement
, showContent
, useShortEmptyTags
, useInlineTags
, defaultConfigPP
, prettyConfigPP
, ConfigPP(..)
) where
import Data.List (intersperse)
import Data.Text (Text)
import qualified Data.Text as T
import TextBuilder (TextBuilder, char, text, toText)
import Text.Pandoc.XML.Light.Types
xmlHeader :: Text
= Text
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
data ConfigPP = ConfigPP
{ ConfigPP -> QName -> Bool
shortEmptyTag :: QName -> Bool
, ConfigPP -> QName -> Bool
inlineTag :: QName -> Bool
, ConfigPP -> Bool
prettify :: Bool
}
defaultConfigPP :: ConfigPP
defaultConfigPP :: ConfigPP
defaultConfigPP = ConfigPP { shortEmptyTag :: QName -> Bool
shortEmptyTag = Bool -> QName -> Bool
forall a b. a -> b -> a
const Bool
True
, inlineTag :: QName -> Bool
inlineTag = Bool -> QName -> Bool
forall a b. a -> b -> a
const Bool
False
, prettify :: Bool
prettify = Bool
False
}
useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP
useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP
useShortEmptyTags QName -> Bool
p ConfigPP
c = ConfigPP
c { shortEmptyTag = p }
useInlineTags :: (QName -> Bool) -> ConfigPP -> ConfigPP
useInlineTags :: (QName -> Bool) -> ConfigPP -> ConfigPP
useInlineTags QName -> Bool
p ConfigPP
c = ConfigPP
c { inlineTag = p }
useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP
Bool
p ConfigPP
c = ConfigPP
c { prettify = p }
prettyConfigPP :: ConfigPP
prettyConfigPP :: ConfigPP
prettyConfigPP = Bool -> ConfigPP -> ConfigPP
useExtraWhiteSpace Bool
True ConfigPP
defaultConfigPP
ppTopElement :: Element -> Text
ppTopElement :: Element -> Text
ppTopElement = ConfigPP -> Element -> Text
ppcTopElement ConfigPP
prettyConfigPP
ppElement :: Element -> Text
ppElement :: Element -> Text
ppElement = ConfigPP -> Element -> Text
ppcElement ConfigPP
prettyConfigPP
ppContent :: Content -> Text
ppContent :: Content -> Text
ppContent = ConfigPP -> Content -> Text
ppcContent ConfigPP
prettyConfigPP
ppcTopElement :: ConfigPP -> Element -> Text
ppcTopElement :: ConfigPP -> Element -> Text
ppcTopElement ConfigPP
c Element
e = [Text] -> Text
T.unlines [Text
xmlHeader,ConfigPP -> Element -> Text
ppcElement ConfigPP
c Element
e]
ppcElement :: ConfigPP -> Element -> Text
ppcElement :: ConfigPP -> Element -> Text
ppcElement ConfigPP
c = TextBuilder -> Text
toText (TextBuilder -> Text)
-> (Element -> TextBuilder) -> Element -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS ConfigPP
c TextBuilder
forall a. Monoid a => a
mempty
ppcContent :: ConfigPP -> Content -> Text
ppcContent :: ConfigPP -> Content -> Text
ppcContent ConfigPP
c = TextBuilder -> Text
toText (TextBuilder -> Text)
-> (Content -> TextBuilder) -> Content -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigPP -> TextBuilder -> Content -> TextBuilder
ppContentS ConfigPP
c TextBuilder
forall a. Monoid a => a
mempty
type Indent = TextBuilder
ppContentS :: ConfigPP -> Indent -> Content -> TextBuilder
ppContentS :: ConfigPP -> TextBuilder -> Content -> TextBuilder
ppContentS ConfigPP
c TextBuilder
i Content
x = case Content
x of
Elem Element
e -> ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS ConfigPP
c TextBuilder
i Element
e
Text CData
t -> ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS ConfigPP
c TextBuilder
i CData
t
CRef Text
r -> Text -> TextBuilder
showCRefS Text
r
ppElementS :: ConfigPP -> Indent -> Element -> TextBuilder
ppElementS :: ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS ConfigPP
c TextBuilder
i Element
e = TextBuilder
i TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS' ConfigPP
c TextBuilder
i Element
e
ppElementS' :: ConfigPP -> Indent -> Element -> TextBuilder
ppElementS' :: ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS' ConfigPP
c TextBuilder
i Element
e = QName -> [Attr] -> TextBuilder
tagStart (Element -> QName
elName Element
e) (Element -> [Attr]
elAttribs Element
e) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
(case Element -> [Content]
elContent Element
e of
[] | Text
"?" Text -> Text -> Bool
`T.isPrefixOf` QName -> Text
qName QName
name -> Text -> TextBuilder
text Text
" ?>"
| ConfigPP -> QName -> Bool
shortEmptyTag ConfigPP
c QName
name -> Text -> TextBuilder
text Text
" />"
[Text CData
t] -> Char -> TextBuilder
char Char
'>' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS' ConfigPP
c (if ConfigPP -> QName -> Bool
inlineTag ConfigPP
c QName
name then TextBuilder
i else TextBuilder
forall a. Monoid a => a
mempty) CData
t TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
QName -> TextBuilder
tagEnd QName
name
[Content]
cs | ConfigPP -> QName -> Bool
inlineTag ConfigPP
c QName
name ->
Char -> TextBuilder
char Char
'>' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat ((Content -> TextBuilder) -> [Content] -> [TextBuilder]
forall a b. (a -> b) -> [a] -> [b]
map Content -> TextBuilder
inlineContent [Content]
cs) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> QName -> TextBuilder
tagEnd QName
name
| Bool
otherwise ->
Char -> TextBuilder
char Char
'>' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
nl TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
[TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat ((Content -> TextBuilder) -> [Content] -> [TextBuilder]
forall a b. (a -> b) -> [a] -> [b]
map ((TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
nl) (TextBuilder -> TextBuilder)
-> (Content -> TextBuilder) -> Content -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigPP -> TextBuilder -> Content -> TextBuilder
ppContentS ConfigPP
c (TextBuilder
sp TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
i)) [Content]
cs) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
TextBuilder
i TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> QName -> TextBuilder
tagEnd QName
name
where (TextBuilder
nl,TextBuilder
sp) = if ConfigPP -> Bool
prettify ConfigPP
c
then (Text -> TextBuilder
text Text
"\n", Text -> TextBuilder
text Text
" ")
else (TextBuilder
forall a. Monoid a => a
mempty, TextBuilder
forall a. Monoid a => a
mempty)
inlineContent :: Content -> TextBuilder
inlineContent (Elem Element
el) = ConfigPP -> TextBuilder -> Element -> TextBuilder
ppElementS' ConfigPP
c TextBuilder
i Element
el
inlineContent (Text CData
t) = ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS' ConfigPP
c TextBuilder
i CData
t
inlineContent (CRef Text
r) = Text -> TextBuilder
showCRefS Text
r
)
where name :: QName
name = Element -> QName
elName Element
e
ppCDataS :: ConfigPP -> Indent -> CData -> TextBuilder
ppCDataS :: ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS ConfigPP
c TextBuilder
i CData
t = TextBuilder
i TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS' ConfigPP
c TextBuilder
i CData
t
ppCDataS' :: ConfigPP -> Indent -> CData -> TextBuilder
ppCDataS' :: ConfigPP -> TextBuilder -> CData -> TextBuilder
ppCDataS' ConfigPP
c TextBuilder
i CData
t = if CData -> CDataKind
cdVerbatim CData
t CDataKind -> CDataKind -> Bool
forall a. Eq a => a -> a -> Bool
/= CDataKind
CDataText Bool -> Bool -> Bool
|| Bool -> Bool
not (ConfigPP -> Bool
prettify ConfigPP
c)
then CData -> TextBuilder
showCDataS CData
t
else [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat
(TextBuilder -> [TextBuilder] -> [TextBuilder]
forall a. a -> [a] -> [a]
intersperse (Char -> TextBuilder
char Char
'\n' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
i)
((Text -> TextBuilder) -> [Text] -> [TextBuilder]
forall a b. (a -> b) -> [a] -> [b]
map Text -> TextBuilder
escStr
((Char -> Bool) -> Text -> [Text]
T.split (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'\n') (CData -> Text
cdData CData
t))))
showTopElement :: Element -> Text
showTopElement :: Element -> Text
showTopElement Element
c = Text
xmlHeader Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Element -> Text
showElement Element
c
showContent :: Content -> Text
showContent :: Content -> Text
showContent = ConfigPP -> Content -> Text
ppcContent ConfigPP
defaultConfigPP
showElement :: Element -> Text
showElement :: Element -> Text
showElement = ConfigPP -> Element -> Text
ppcElement ConfigPP
defaultConfigPP
showCRefS :: Text -> TextBuilder
showCRefS :: Text -> TextBuilder
showCRefS Text
r = Char -> TextBuilder
char Char
'&' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
text Text
r TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Char -> TextBuilder
char Char
';'
showCDataS :: CData -> TextBuilder
showCDataS :: CData -> TextBuilder
showCDataS CData
cd =
case CData -> CDataKind
cdVerbatim CData
cd of
CDataKind
CDataText -> Text -> TextBuilder
escStr (CData -> Text
cdData CData
cd)
CDataKind
CDataVerbatim -> Text -> TextBuilder
text Text
"<![CDATA[" TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
escCData (CData -> Text
cdData CData
cd) TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
Text -> TextBuilder
text Text
"]]>"
CDataKind
CDataRaw -> Text -> TextBuilder
text (CData -> Text
cdData CData
cd)
escCData :: Text -> TextBuilder
escCData :: Text -> TextBuilder
escCData Text
t =
case HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
T.breakOn Text
"]]>" Text
t of
(Text
chunk, Text
rest)
| Text -> Bool
T.null Text
rest -> Text -> TextBuilder
text Text
chunk
| Bool
otherwise -> Text -> TextBuilder
text Text
chunk TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
text Text
"]]]]><![CDATA[>" TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
Text -> TextBuilder
escCData (Int -> Text -> Text
T.drop Int
3 Text
rest)
escChar :: Char -> TextBuilder
escChar :: Char -> TextBuilder
escChar Char
c = case Char
c of
Char
'<' -> Text -> TextBuilder
text Text
"<"
Char
'>' -> Text -> TextBuilder
text Text
">"
Char
'&' -> Text -> TextBuilder
text Text
"&"
Char
'"' -> Text -> TextBuilder
text Text
"""
Char
'\'' -> Text -> TextBuilder
text Text
"'"
Char
_ -> Char -> TextBuilder
char Char
c
escStr :: Text -> TextBuilder
escStr :: Text -> TextBuilder
escStr Text
cs = case (Char -> Bool) -> Text -> (Text, Text)
T.break Char -> Bool
needsEscape Text
cs of
(Text
chunk, Text
rest) ->
case Text -> Maybe (Char, Text)
T.uncons Text
rest of
Maybe (Char, Text)
Nothing -> Text -> TextBuilder
text Text
chunk
Just (Char
c, Text
rest') ->
Text -> TextBuilder
text Text
chunk TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Char -> TextBuilder
escChar Char
c TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
escStr Text
rest'
where
needsEscape :: Char -> Bool
needsEscape Char
'<' = Bool
True
needsEscape Char
'>' = Bool
True
needsEscape Char
'&' = Bool
True
needsEscape Char
'"' = Bool
True
needsEscape Char
'\'' = Bool
True
needsEscape Char
_ = Bool
False
tagEnd :: QName -> TextBuilder
tagEnd :: QName -> TextBuilder
tagEnd QName
qn = Text -> TextBuilder
text Text
"</" TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> QName -> TextBuilder
showQName QName
qn TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Char -> TextBuilder
char Char
'>'
tagStart :: QName -> [Attr] -> TextBuilder
tagStart :: QName -> [Attr] -> TextBuilder
tagStart QName
qn [Attr]
as = Char -> TextBuilder
char Char
'<' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> QName -> TextBuilder
showQName QName
qn TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> TextBuilder
as_str
where as_str :: TextBuilder
as_str = if [Attr] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Attr]
as
then TextBuilder
forall a. Monoid a => a
mempty
else [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat ((Attr -> TextBuilder) -> [Attr] -> [TextBuilder]
forall a b. (a -> b) -> [a] -> [b]
map Attr -> TextBuilder
showAttr [Attr]
as)
showAttr :: Attr -> TextBuilder
showAttr :: Attr -> TextBuilder
showAttr (Attr QName
qn Text
v) = Char -> TextBuilder
char Char
' ' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> QName -> TextBuilder
showQName QName
qn TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
Char -> TextBuilder
char Char
'=' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<>
Char -> TextBuilder
char Char
'"' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
escStr Text
v TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Char -> TextBuilder
char Char
'"'
showQName :: QName -> TextBuilder
showQName :: QName -> TextBuilder
showQName QName
q =
case QName -> Maybe Text
qPrefix QName
q of
Maybe Text
Nothing -> Text -> TextBuilder
text (QName -> Text
qName QName
q)
Just Text
p -> Text -> TextBuilder
text Text
p TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Char -> TextBuilder
char Char
':' TextBuilder -> TextBuilder -> TextBuilder
forall a. Semigroup a => a -> a -> a
<> Text -> TextBuilder
text (QName -> Text
qName QName
q)