tintin-1.9.0: src/Tintin/Render.hs
module Tintin.Render where
import Tintin.Core
require Tintin.Capabilities.Logging
require Tintin.Capabilities.Filesystem
require Tintin.Capabilities.Process
require Tintin.Domain.HtmlFile
require Tintin.Domain.DocumentationFile
require Tintin.Domain.Project
require Tintin.Html.Templating
require Tintin.Errors
require Data.Map
require Data.Text
data Render
perform :: ( Has Logging.Capability eff
, Has Filesystem.Capability eff
, Has Process.Capability eff
)
=> HtmlFile.BuildTool
-> [DocumentationFile]
-> Effectful eff [HtmlFile]
perform buildTool docFiles = do
Logging.debug "Rendering"
(errors, htmlFiles) <- docFiles
|> map HtmlFile.fromDocumentationFile
|> mapM (HtmlFile.run buildTool)
|$> partitionEithers
unless (null errors) (Errors.textDie (HtmlFile.showCompilationError <$> errors))
return htmlFiles
writeOutput :: ( Has Logging.Capability eff
, Has Filesystem.Capability eff
)
=> OutputDirectory
-> Project.Info
-> Effectful eff ()
writeOutput (OutputDirectory od) info = do
Filesystem.makeDirectory (Filesystem.Path od)
Logging.debug "Writing HTML output"
forM_ (withContext (Project.pages info)) $ \(page, context) -> do
let newContent = Templating.wrap info context page
let slash = if "/" `Text.isSuffixOf` od then "" else "/"
Filesystem.writeFile (Filesystem.Path $ od <> slash <> Project.filename page) newContent
-- | Pair up each page with context (next and previous links) by
-- alphabetical order, excluding index page.
--
-- This function is a little more complicated than might be expected
-- because the functionality is a little nuanced. It has to:
--
-- 1. Pair each page with next and previous links based on an alphabetical
-- sorting, /excluding/ the index page.
-- 2. Give the /first/ non-index page, alphabetically, a "previous" link
-- pointing to the index /if it exists/.
-- 3. Give the index, if it exists, a "next' link to the first non-index
-- page.
--
withContext
:: [Project.Page]
-> [(Project.Page, Project.Context)]
withContext ps = elems contextMap
where
-- | Pre-processing function. Using a single fold, pick out the index
-- page link (if it exists) and also accumulate a Map of all non-index
-- pages, keyed by filenames. The map enforces that the filenames are
-- stored alphabetically.
indexRef :: Maybe Project.PageRef
pageMap :: Map Text Project.Page
(First indexRef, pageMap)
= foldMap (\p -> let fn = Project.filename p
in if fn == "index.html"
then (First (Just (makeRef p)), mempty )
else (mempty , one (fn, p))
) ps
-- | Accumulate a final result map pairing each file with its
-- 'makeContext' result. The keys are the filenames, to enforce
-- alphabetical sorting.
contextMap :: Map Text (Project.Page, Project.Context)
contextMap = ps
|$> (\p -> (Project.filename p, (p, makeContext p)))
|> Map.fromList
-- | Actual function that pairs up each page with its context (next and
-- previous links).
--
-- If the page is "index.html", use just the first item in 'pageMap',
-- the map of non-index pages, using 'Map.lookupMin'
--
-- Otherwise, use 'Map.lookupLT' and 'Map.lookupGT' to find the "next
-- and previous" pages in 'pageMap' (the map of non-index pages). And,
-- if there is no previous page, use the index page instead.
makeContext :: Project.Page -> Project.Context
makeContext p
| fn == "index.html" = Map.lookupMin pageMap
|$> snd
|$> makeRef
|> Project.Context Nothing
| otherwise =
let prev = Map.lookupLT fn pageMap
|$> snd
|$> makeRef
next = Map.lookupGT fn pageMap
|$> snd
|$> makeRef
in Project.Context (prev <|> indexRef) next -- if no prev, use index
where
fn = Project.filename p
-- | Construct a link/reference from a page.
makeRef :: Project.Page -> Project.PageRef
makeRef p = Project.PageRef
{ refTitle = Project.title p
, refFilename = Project.filename p
}