lhs2html-0.99999: src/lhs2html.hs
{-# LANGUAGE Haskell2010, LambdaCase #-}
module Main where
import Data.Functor
import Data.List
import Control.Monad
import System.Environment
import Text.Nicify
import System.Directory
import System.FilePath
import System.FilePath.Glob
import Paths_lhs2html
main = getArgs >>= \case
m : args
| m == "-m" || m == "--markdown" -> run lhs2md "md" args
| m == "-u" || m == "--unlit" -> run lhs2hs "hs" args
| m == "-p" || m == "--parsetree" -> run lhs2txt "txt" args
| m == "-v" || m == "--version" -> putStrLn "lhs2html v0.99999"
| m == "-h" || m == "--help" -> putStrLn "\
\ -m --markdown transform to github flavored markdown (*.md)\n\
\ -u --unlint plain unlit to *.hs\n\
\ -p --parsetree show parse tree as *.txt\n\
\ -h --help this help\n\
\ -v --version show version information"
args -> run lhs2html "htm" args
run processor suffix = \case
[] -> processor <$> getContents >>= putStrLn
args -> do
header <- getDataFileName ("data" </> "header" <.> suffix) >>= readFile
footer <- getDataFileName ("data" </> "footer" <.> suffix) >>= readFile
let process f = do
isFile <- doesFileExist f
isDir <- doesDirectoryExist f
when isDir (globDir1 (compile "*.lhs") f >>= mapM_ process)
when isFile $ do
let outfile = f <.> suffix
writeFile outfile header
processor <$> readFile f >>= appendFile outfile
appendFile outfile footer
mapM_ process args
lhs2txt = nicify . show . parse
lhs2html = foldr toHTML "" . parse
lhs2md = concat . intersperse "\n" . filter (not . null) . map toMarkdown . parse
where
nl x y = x ++ '\n' : y
infixr 5 `nl`
toMarkdown = \case
Code xs -> unlines $ ("```haskell" : xs ++ ["```"])
Para xs -> unlines xs
Quote xs -> unlines xs
OrdList xs -> unlines $ map ("1. " ++) xs
List xs -> unlines $ map ("* " ++) xs
H1 x -> x `nl` replicate (length x) '=' `nl` ""
H2 x -> x `nl` replicate (length x) '-' `nl` ""
H3 x -> "### " ++ x `nl` ""
_ -> []
lhs2hs = unlines . concatMap (\case { Code xs -> xs; _ -> [] }) . parse
parse :: String -> [Object]
parse = process . map identify . lines
data Object =
Empty
| Para [String]
| Quote [String]
| Code [String]
| OrdList [String]
| List [String]
| H1 String
| H2 String
| H3 String
| H1' | H2' | H3'
deriving (Eq, Show)
identify :: String -> Object
identify = \case
'>' : ' ' : xs -> Code [xs]
'>' : xs -> Code [xs]
'-' : xs
| all (== '-') xs -> H2'
'-' : ' ' : xs -> List [xs]
line@('=' : xs)
| all (== '=') xs -> H1'
| otherwise -> Para [line]
'+' : ' ' : xs -> OrdList [xs]
line@(' ' : xs)
| all (== ' ') xs -> Empty
| otherwise -> Quote [line]
'#' : '#' : '#' : ' ' : xs -> H3 xs
'#' : '#' : ' ' : xs -> H2 xs
'#' : ' ' : xs -> H1 xs
[] -> Empty
xs -> Para [xs]
process :: [Object] -> [Object]
process = \case
Empty : xs@(Empty : _) -> process xs
Para x : Para y : xs -> process (Para (x ++ y) : xs)
Para x : H1' : xs -> process (Para (init x) : H1 (last x) : xs)
Para x : H2' : xs -> process (Para (init x) : H2 (last x) : xs)
Para x : H3' : xs -> process (Para (init x) : H3 (last x) : xs)
OrdList x : OrdList y : xs -> process (OrdList (x ++ y) : xs)
List x : List y : xs -> process (List (x ++ y) : xs)
Quote x : Quote y : xs -> process (Quote (x ++ y) : xs)
Code x : Code y : xs -> process (Code (x ++ y) : xs)
x : xs -> x : process xs
[] -> []
toHTML :: Object -> ShowS
toHTML obj xs = case obj of
Para ls -> mkBlock "p" htmlize ls xs
Code ls -> mkBlock "pre" (spanify . escape) ls xs
List ls -> mkBlock "ul" (itemize . htmlize) ls xs
OrdList ls -> mkBlock "ol" (itemize . htmlize) ls xs
Quote ls -> mkBlock "blockquote" (spanify . escape) ls xs
H1 ls -> "<h1>" ++ htmlize ls ++ "</h1>\n" ++ xs
H2 ls -> "<h2>" ++ htmlize ls ++ "</h2>\n" ++ xs
H3 ls -> "<h3>" ++ htmlize ls ++ "</h3>\n" ++ xs
_ -> xs
where
mkBlock :: String -> (String -> String) -> [String] -> ShowS
mkBlock tag f ls xs = foldr (++) ("</" ++ tag ++ ">\n" ++ xs) (("<" ++ tag ++ ">") : map' f ls)
itemize xs = "<li>" ++ xs ++ "</li>"
spanify [] = ""
spanify xs = "<span>" ++ xs ++ "</span>"
map' f = foldr (\a b -> (f a ++ "\n") : b) []
htmlize = codify False . escape
escape = concatMap $ \case
'<' -> "<"
'>' -> ">"
'&' -> "&"
x -> [x]
codify False ('`' : xs) = "<code>" ++ codify True xs
codify True ('`' : xs) = "</code>" ++ codify False xs
codify flag (x : xs) = x : codify flag xs
codify _ _ = []