wembley-0.1.0.0: src/Latex.hs
{-# LANGUAGE OverloadedStrings #-}
module Latex
( render
) where
import Data.Monoid
import qualified Data.ByteString.Char8 as C
import qualified System.FilePath as F
-- | Translate an extension into a Minted language name. Note that
-- extensions that are not recognized will be rendered without any
-- syntax highlighting.
translateExt :: String -- ^ file extension
-> C.ByteString -- ^ Minted language
translateExt ext
| ext == "c" || ext == "h" = "c"
| ext == "sh" = "bash"
| ext == "hs" = "haskell"
| ext == "pl" = "perl"
| ext == "py" = "python"
| ext == "rb" = "ruby"
| ext == "java" = "java"
| otherwise = "text"
-- | Append newline as the last character of a string if it does not end
-- with one.
ensureNewline :: C.ByteString -- ^ old string
-> C.ByteString -- ^ new string
ensureNewline str
| C.null str = str
| C.last str == '\n' = str
| otherwise = str <> "\n"
-- | All underscores must be escaped in order to prevent the subscript
-- redenring.
escapeUnderscore :: String -- ^ old string
-> String -- ^ new string
escapeUnderscore [] = []
escapeUnderscore (x:xs)
| x == '_' = '\\':x:escapeUnderscore xs
| otherwise = x:escapeUnderscore xs
-- | Apply decoration to a single file and its contents.
decorateFile :: (String, C.ByteString) -- ^ file name & content
-> C.ByteString -- ^ decorated file
decorateFile (name, content) = C.unlines
[ "\\section*{" <> name' <> "}"
, "\\addcontentsline{toc}{subsection}{" <> name' <> "}"
, "\\begin{minted}{" <> translateExt (tail $ F.takeExtensions name) <> "}"
, ensureNewline content <> "\\end{minted}"
, "\n" ]
where name' = C.pack $ escapeUnderscore name
-- | Wrap the document in a standard envelope.
envelope :: String -- ^ project name
-> C.ByteString -- ^ content
-> C.ByteString -- ^ finished document
envelope name content = C.unlines
[ "\\documentclass{article}"
, "\\usepackage{minted}"
, "\\usepackage{fullpage}"
, "\\begin{document}"
, "\\centerline{\\bf{\\Huge{" <> name' <> "}}}"
, "\\bigskip"
, "\\tableofcontents"
, "\\bigskip"
, content
, "\\end{document}" ]
where name' = C.pack $ escapeUnderscore name
-- | Apply decoration to a whole codebase in order to create a document.
render :: String -- ^ project name
-> [(String, C.ByteString)] -- ^ file names & contents
-> C.ByteString -- ^ final document
render name entries = envelope name (C.unlines $ map decorateFile entries)