lit
fileHtml is a module which transforms the main data structure for a parsed
lit
file into Html. Html renders the narrative from markdown to Html
and introduces anchors to refer back and forth from macros references
to their definitions. Html generates simple
valid html. It currently does not create a table of contents or implement
any other extra functionality.
An overview of the file:
<< * >>=
<< define html module >>
<< import modules >>
<< generate html document from chunks >>
<< helper functions >>
Html only exports generate
akin to Code and Markdown, the other output modules.
<< define html module >>=
{-# LANGUAGE OverloadedStrings #-}
module Html (generate) where
Html relies on Highlight to provide syntax highlighting and Cheapskate to
render the markdown in the narrative of lit
files. The Blaze libraries
generate the Html from handy combinators.
<< import modules >>=
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Data.Maybe (fromMaybe)
import Text.Blaze (toValue, (!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Cheapskate (markdown, def)
import Cheapskate.Html
import Highlight
import Types
generate
is the interface for the module. lit
generates the [Chunk]
with Parse.hs
before calling generate
to make the Html file from a lit
file. It performs the following:
Prose
into a single Prose
Chunk
to html<< generate html document from chunks >>=
generate :: Maybe String -> String -> [Chunk] -> T.Text
generate maybeCss name chunks =
let
lang = getLang name
mergedProse = simplify chunks -- adjacent Prose combined to one prose
body = H.preEscapedToHtml $ map (chunkToHtml lang) mergedProse
doc = preface maybeCss name body
in
TL.toStrict $ renderHtml doc
An overview of the helper functions used to generate a valid html document.
<< helper functions >>=
<< frequently used operator for appending text >>
<< preface the html with the head tag >>
<< simplify consecutive prose chunks >>
<< transform a chunk to html >>
<< transform a part of a chunk to html >>
<< wrap a chunk header in an anchor >>
<< replace spaces with underscores in anchors >>
Due to the verbosity of using T.append
as an infix operator for T.Text
, the following
operator appends text like the list append operator ++
<< frequently used operator for appending text >>=
(<++>) :: T.Text -> T.Text -> T.Text
(<++>) = T.append
preface
wraps the generated lit html in the html necessary
for all valid html documents. On the command line lit
accepts
a --css=path/to/file
option which will include css in the prefaced document
<< preface the html with the head tag >>=
preface :: Maybe String -> String -> H.Html -> H.Html
preface maybeCss fileName bodyHtml =
let
cssPath = fromMaybe "" maybeCss
cssAttr = toValue cssPath
includeCss =
if cssPath /= ""
then H.link ! A.rel "stylesheet" ! A.type_ "text/css" ! A.href cssAttr
else H.toHtml T.empty
in
H.docTypeHtml $ do
H.head $ do
H.title $ H.toHtml fileName
H.meta ! A.charset "UTF-8"
includeCss
H.body $ do bodyHtml
Before Chunk
s can be rendered to html, all consecutive Prose
s are
reduced to a single Prose
. Many consecutive Prose
s exist because parsing the lit
file
by line greatly simplifies the parser. As a result each line of narrative is
stored as a Prose
of that line. When rendering a Prose
, it is necessary that the entire
prose is passed together to be rendered. A markdown list rendered separately in markdown
would result in several lists in the resulting html.
<< simplify consecutive prose chunks >>=
simplify :: [Chunk] -> [Chunk]
simplify [] = []
simplify lst =
let
(defs, ps) = span isDef lst
(ps', rest) = break isDef ps
mergeProse chunks = Prose $ T.concat $ map getProseText chunks
in case ps' of
[] -> defs ++ rest
_ -> defs ++ [mergeProse ps'] ++ (simplify rest)
For a Chunk
, the Prose
is rendered through the markdown renderer.
The other form, Def
renders its name with an anchor and renders its parts within
a code block. Because the syntax for lit
macros conflicts with html, these characters are
escaped with <
and >
<< transform a chunk to html >>=
chunkToHtml :: String -> Chunk -> H.Html
chunkToHtml lang chunk =
case chunk of
Prose txt -> H.toHtml $ markdown def txt
Def _ name parts ->
let
header = headerToHtml name
htmlParts = H.preEscapedToHtml $ map (partToHtml lang) parts
in
H.pre $ H.code $ (header >> htmlParts)
A part can either be a line of code, or a reference to a macro. In the resulting html, the reference turns into an anchor pointing to the macro's definition. The line of code is simply highlighted.
<< transform a part of a chunk to html >>=
partToHtml :: String -> Part -> H.Html
partToHtml lang part =
case part of
Code txt -> highlight lang txt
Ref txt indent -> H.preEscapedToHtml (indent <++> "<< " <++> link <++> " >>\n")
where
link = "<a href=\"#" <++> underscored <++> "\">" <++> slim <++> "</a>"
slim = T.strip txt
underscored = underscore slim
A macro definition like the one below, is rendered as an anchor with an id, to be referred to by other inline anchors. This allows any macro reference to redirect the reader to its definition.
<< wrap a chunk header in an anchor >>=
headerToHtml :: T.Text -> H.Html
headerToHtml name = H.preEscapedToHtml $ "<< " <++> link <++> " >>=\n"
where
link = "<a id=\"" <++> underscored <++> "\" href=\"#" <++> underscored <++> "\">" <++> slim <++> "</a>"
slim = T.strip name
underscored = underscore slim
In order to have valid html, an id
attribute and an href
attribute can not contain spaces.
However, in a lit
file macro names can contain spaces. This helper method replaces spaces with
an underscore in the macro names when creating valid anchors.
<< replace spaces with underscores in anchors >>=
underscore :: T.Text -> T.Text
underscore txt =
T.pack $ concatMap (\c -> if c == ' ' then "_" else [c]) $ T.unpack txt