Writer.hs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. module Text.Ptolemy.HTML.Writer (writeHtml, writeHtmlStrict) where
  2. import Data.Monoid ((<>))
  3. import qualified Data.Text as TS
  4. import Data.Text.Lazy (Text, toStrict)
  5. import Data.Text.Lazy.Builder (Builder)
  6. import qualified Data.Text.Lazy.Builder as B
  7. import Data.Vector (Vector)
  8. import qualified Data.Vector as V
  9. import Text.Ptolemy.Core
  10. -- | Render a Ptolemy @Document@ as HTML represented as lazy @Text@.
  11. writeHtml :: Document -> Text
  12. writeHtml = B.toLazyText . build
  13. -- | Render a Ptolemy @Document@ as HTML represented as strict @Text@.
  14. writeHtmlStrict :: Document -> TS.Text
  15. writeHtmlStrict = toStrict . writeHtml
  16. -- These will be our helper functions for building tags
  17. tag :: Text -> Builder -> Builder
  18. tag t bs = "<" <> build t <> ">" <> bs <> "</" <> build t <> ">"
  19. tagAttrs :: Text -> [(TS.Text, TS.Text)] -> Builder -> Builder
  20. tagAttrs t [] bs = tag t bs
  21. tagAttrs t as bs =
  22. "<" <> build t <> attrs as <> ">" <> bs <> "</" <> build t <> ">"
  23. where attrs [] = mempty
  24. attrs ((k,v):xs) =
  25. " " <> build k <> "=\"" <> build v <> "\"" <> attrs xs
  26. -- Right now, this just makes the code below a lot smaller: we
  27. -- abstract out the notion of 'building' a thing, so we can
  28. -- more or less indiscriminately apply @build@ to vectors or
  29. -- maps or what-have-you.
  30. class Build t where
  31. build :: t -> Builder
  32. -- And to that end, we define a handful of utility
  33. -- implementations of things:
  34. instance Build t => Build (Vector t) where
  35. build = foldMap build
  36. instance Build Text where
  37. build = B.fromLazyText
  38. instance Build TS.Text where
  39. build = B.fromText
  40. instance Build Block where
  41. build (Plain cs) = build cs
  42. build (Para cs) = tag "p" $ build cs
  43. build (CodeBlock _ ts) = tag "pre" $ tag "code" $ build ts
  44. build (RawBlock _ _) = undefined
  45. build (BlockQuote bs) = tag "blockquote" $ build bs
  46. build (OrderedList la ds) =
  47. tagAttrs "ol" (orderedListAttrs la) $ foldMap (tag "li" . build) ds
  48. build (BulletList ds) =
  49. tag "ul" $ foldMap (tag "li" . build) ds
  50. build (DefinitionList ds) =
  51. tag "dl" $ foldMap build ds
  52. build (Header n _ is) =
  53. case n of
  54. 1 -> tag "h1" $ build is
  55. 2 -> tag "h2" $ build is
  56. 3 -> tag "h3" $ build is
  57. 4 -> tag "h4" $ build is
  58. _ -> undefined -- XXX
  59. build HorizontalRule = "<hr/>"
  60. build (Div as ds) = tagAttrs "div" (mkAttrs as) $ build ds
  61. build Null = mempty
  62. whitesep :: Foldable f => f TS.Text -> TS.Text
  63. whitesep = foldl sep ""
  64. where sep x y = x <> " " <> y
  65. mkAttrs :: Attr -> [(TS.Text, TS.Text)]
  66. mkAttrs Attr { attrIdentifier = ai
  67. , attrClasses = cs
  68. , attrProps = _ -- XXX
  69. } = htmlId <> htmlCs <> htmlPs
  70. where htmlId | ai /= "" = [("id", ai)]
  71. | otherwise = []
  72. htmlCs | V.null cs = []
  73. | otherwise = [("class", whitesep cs)]
  74. htmlPs = []
  75. orderedListAttrs :: ListAttributes -> [(TS.Text, TS.Text)]
  76. orderedListAttrs _ = [("style", "list-style-type: decimal")]
  77. instance Build Definition where
  78. build Definition
  79. { dfTerm = term
  80. , dfDefinition = defn
  81. } = tag "dt" (build term) <> tag "dd" (build defn)
  82. instance Build Inline where
  83. build (Str t) = build t
  84. build (Emph is) = tag "em" $ build is
  85. build (Strong is) = tag "strong" $ build is
  86. build (Strikeout is) = tag "del" $ build is
  87. build (Superscript is) = tag "span" $ build is -- XXX
  88. build (Subscript is) = tag "span" $ build is -- XXX
  89. build (SmallCaps is) = tag "span" $ build is -- XXX
  90. build Space = " "
  91. build (Code _ t) = tag "code" $ build t
  92. build SoftBreak = mempty
  93. build LineBreak = "<br/>"