Skip to content

Commit

Permalink
Docs HTML renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Mar 9, 2017
1 parent 1822eb7 commit eb3c973
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nirum.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ library
, Nirum.Constructs.TypeDeclaration
, Nirum.Constructs.TypeExpression
, Nirum.Docs
, Nirum.Docs.Html
, Nirum.Package
, Nirum.Package.Metadata
, Nirum.Package.ModuleSet
Expand Down Expand Up @@ -115,6 +116,7 @@ test-suite spec
, Nirum.Constructs.TypeDeclarationSpec
, Nirum.Constructs.TypeExpressionSpec
, Nirum.DocsSpec
, Nirum.Docs.HtmlSpec
, Nirum.Package.MetadataSpec
, Nirum.Package.ModuleSetSpec
, Nirum.PackageSpec
Expand Down
9 changes: 9 additions & 0 deletions src/Nirum/Docs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module Nirum.Docs ( Block ( BlockQuote
, Title
, Url
, headingLevelFromInt
, headingLevelInt
, parse
) where

Expand All @@ -57,6 +58,14 @@ headingLevelFromInt 4 = H4
headingLevelFromInt 5 = H5
headingLevelFromInt i = if i > 5 then H6 else H1

headingLevelInt :: HeadingLevel -> Int
headingLevelInt H1 = 1
headingLevelInt H2 = 2
headingLevelInt H3 = 3
headingLevelInt H4 = 4
headingLevelInt H5 = 5
headingLevelInt H6 = 6

-- | Whether a list is a bullet list or an ordered list.
-- See also: http://spec.commonmark.org/0.25/#of-the-same-type
data ListType = BulletList
Expand Down
78 changes: 78 additions & 0 deletions src/Nirum/Docs/Html.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
module Nirum.Docs.Html (render, renderInline, renderInlines, renderBlock) where

import qualified Data.Text as T
import Text.InterpolatedString.Perl6 (qq)

import Nirum.Docs

renderInline :: Inline -> Html
renderInline (Text t) = escape t
renderInline SoftLineBreak = "\n"
renderInline HardLineBreak = "<br>"
renderInline (HtmlInline html) = html
renderInline (Code code') = [qq|<code>{escape code'}</code>|]
renderInline (Emphasis inlines) = [qq|<em>{renderInlines inlines}</em>|]
renderInline (Strong inlines) = [qq|<strong>{renderInlines inlines}</strong>|]
renderInline (Link url title inlines) =
let body = renderInlines inlines
in
if T.null title
then [qq|<a href="{escape url}">$body</a>|]
else [qq|<a href="{escape url}" title="{escape title}">$body</a>|]
renderInline (Image url title) =
if T.null title
then [qq|<img src="{escape url}">|]
else [qq|<img src="{escape url}" title="{escape title}">|]

escape :: T.Text -> Html
escape = T.concatMap escapeChar

escapeChar :: Char -> Html
escapeChar '&' = "&amp;"
escapeChar '"' = "&quot;"
escapeChar '<' = "&lt;"
escapeChar '>' = "&gt;"
escapeChar c = T.singleton c

renderInlines :: [Inline] -> Html
renderInlines = T.concat . map renderInline

renderBlock :: Block -> Html
renderBlock (Document blocks) = renderBlocks blocks `T.snoc` '\n'
renderBlock ThematicBreak = "<hr>"
renderBlock (Paragraph inlines) = [qq|<p>{renderInlines inlines}</p>|]
renderBlock (BlockQuote blocks) =
[qq|<blockquote>{renderBlocks blocks}</blockquotes>|]
renderBlock (HtmlBlock html) = html
renderBlock (CodeBlock lang code') =
if T.null lang
then [qq|<pre><code>$code'</code></pre>|]
else [qq|<pre><code class="language-$lang">$code'</code></pre>|]
renderBlock (Heading level inlines) =
let lv = headingLevelInt level
in [qq|<h$lv>{renderInlines inlines}</h$lv>|]
renderBlock (List listType itemList) =
let liList = case itemList of
TightItemList items ->
[ [qq|<li>{renderInlines item}</li>|]
| item <- items
]
LooseItemList items ->
[ [qq|<li>{renderBlocks item}</li>|]
| item <- items
]
tag = case listType of
BulletList -> "ul" :: T.Text
OrderedList { startNumber = 1 } -> "ol"
OrderedList { startNumber = startNumber' } ->
[qq|ol start="$startNumber'"|]
nl = '\n'
liListT = T.intercalate "\n" liList
in [qq|<$tag>$nl$liListT$nl</$tag>|]

renderBlocks :: [Block] -> Html
renderBlocks = T.intercalate "\n" . map renderBlock

render :: Block -> Html
render = renderBlock
30 changes: 30 additions & 0 deletions test/Nirum/Docs/HtmlSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{-# LANGUAGE QuasiQuotes #-}
module Nirum.Docs.HtmlSpec where

import Test.Hspec.Meta
import Text.InterpolatedString.Perl6 (q)

import Nirum.Docs (Html)
import Nirum.Docs.Html (render)
import Nirum.DocsSpec (sampleDocument)

expectedHtml :: Html
expectedHtml = [q|<h1>Hello</h1>
<p>Tight list:</p>
<ul>
<li>List test</li>
<li>test2</li>
</ul>
<p>Loose list:</p>
<ol>
<li><p>a</p></li>
<li><p>b</p></li>
</ol>
<p>A <a href="http://nirum.org/" title="Nirum">complex <em>link</em></a>.</p>
|]

spec :: Spec
spec =
describe "Docs.Html" $
specify "render" $
render sampleDocument `shouldBe` expectedHtml

0 comments on commit eb3c973

Please sign in to comment.