wembley-0.1.0.0: src/Markdown.hs
{-# LANGUAGE OverloadedStrings #-}
module Markdown
( render
) where
import Data.Char
import Data.Monoid
import qualified Data.ByteString.Char8 as C
import qualified System.FilePath as F
-- | Translate an extension into a GitHub markdown language name. Note
-- that extensions that are not recognized will be rendered without any
-- syntax highlighting.
translateExt :: String -- ^ file extension
-> C.ByteString -- ^ Markdown language
translateExt ext
| ext == "c" || ext == "h" = "c"
| ext == "sh" = "sh"
| ext == "hs" = "haskell"
| ext == "pl" = "perl"
| ext == "py" = "python"
| ext == "rb" = "ruby"
| ext == "java" = "java"
| otherwise = ""
-- | 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"
-- | 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
[ "## " <> C.pack name
, "```" <> translateExt (tail $ F.takeExtensions name)
, ensureNewline content <> "```" ]
-- | Generate the "table of contents" section
generateTOC :: [String] -- ^ file names
-> C.ByteString -- ^ section content
generateTOC names = C.unlines (header : map (convert . C.pack) names)
where
header = "### Files"
convert name = "* [" <> name <> "](#" <> linkify name <> ")"
linkify = C.map toLower . C.filter (\c -> isAlpha c || c == '_')
-- | 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 = C.unlines
["# " <> C.pack name
, generateTOC $ map fst entries
, C.unlines $ map decorateFile entries ]