-- **Hyakko** is a Haskell port of [docco](http://jashkenas.github.com/docco/): -- the original quick-and-dirty, hundred-line-line, literate-programming-style -- documentation generator. It produces HTML that displays your comments -- alongside your code. Comments are passed through -- [Markdown](http://daringfireball.net/projects/markdown/syntax) and code is -- passed through [Pygments](http://pygments.org/) syntax highlighting. -- This page is the result of running Hyakko against its own source file. -- -- If you install Hyakko, you can run it from the command-line: -- -- hyakko src/*.hs -- -- ...will generate linked HTML documentation for the named source files, saving -- it into a `docs` folder. -- The [source for Hyakko](https://github.com/sourrust/hyakko) available on GitHub. -- -- To install Hyakko -- -- git clone git://github.com/sourrust/hyakko.git -- cd hyakko -- cabal install module Main where import Text.Markdown import Data.Map (Map) import qualified Data.Map as M import Data.List (sort, groupBy) import Data.Maybe (fromJust) import Control.Monad (filterM) import Text.Pandoc.Templates import Text.Regex import Text.Regex.PCRE ((=~)) import System.Directory (getDirectoryContents, doesDirectoryExist, doesFileExist) import System.Environment (getArgs) import System.FilePath (takeBaseName, takeExtension, takeFileName, (>)) import System.Process (system, readProcess) import Paths_hyakko (getDataFileName) -- ### Main Documentation Generation Functions -- Make type signature more readable with these two `Callback` types. type Callback = IO () type Callback' = [Map String String] -> IO () -- Generate the documentation for a source file by reading it in, splitting it -- up into comment/code sections, highlighting them for the appropriate language, -- and merging them into an HTML template. generateDocumentation :: [FilePath] -> IO () generateDocumentation [] = return () generateDocumentation (x:xs) = do code <- readFile x let sections = parse (getLanguage x) code if null sections then putStrLn $ "hyakko doesn't support the language extension " ++ takeExtension x else highlight x sections $ \y -> do generateHTML x y generateDocumentation xs -- Given a string of source code, parse out each comment and the code that -- follows it, and create an individual **section** for it. -- Sections take the form: -- -- [ -- ("docsText", ...), -- ("docsHtml", ...), -- ("codeText", ...), -- ("codeHtml", ...) -- ] -- inSections :: [String] -> String -> [Map String String] inSections xs r = let mkRegex' = mkRegex . (=~ r) -- Generalized function used to section off code and comments groupBy' t t1 = groupBy $ \x y -> and $ map (t1 . (=~ r)) [t x, t y] -- Replace the beggining comment symbol with nothing replace = unlines . map (\y -> subRegex (mkRegex' y) y "") -- Clump sectioned off lines into doc and code text. clump [] = [] clump [x] = clump $ ensurePair [x] clump (x:y:ys) = [("docsText", replace x),("codeText", unlines y)] : clump ys -- Make sure the result is in the right pairing order ensurePair ys = if even $ length ys then ys else if (head . head) ys =~ r then ys ++ [[""]] else [""]:ys -- Group comments into a list s1 = groupBy' id id xs -- Group code into a list s2 = groupBy' head not s1 -- Bring the lists together into groups of comment and groups of code -- pattern. s3 = ensurePair $ map concat s2 in [M.fromList l | l <- clump s3] parse :: Maybe (Map String String) -> String -> [Map String String] parse Nothing _ = [] parse (Just src) code = let line = filter ((/=) "#!" . take 2) $ lines code in inSections line $ src M.! "comment" -- Highlights a single chunk of Haskell code, using **Pygments** over stdio, -- and runs the text of its corresponding comment through **Markdown**, using the -- Markdown translator in **[Pandoc](http://johnmacfarlane.net/pandoc/)**. -- -- We process the entire file in a single call to Pygments by inserting little -- marker comments between each section and then splitting the result string -- wherever our markers occur. highlight :: FilePath -> [Map String String] -> Callback' -> IO () highlight src section cb = do let language = fromJust $ getLanguage src options = ["-l", language M.! "name", "-f", "html", "-O", "encoding=utf-8"] input = concatMap (\x -> x M.! "codeText"++(language M.! "dividerText")) section output <- readProcess "pygmentize" options input let output' = subRegex (mkRegex highlightReplace) output "" fragments = splitRegex (mkRegex $ language M.! "dividerHtml") output' cb $ map (\x -> let s = section !! x in M.insert "docsHtml" (toHTML $ s M.! "docsText") $ M.insert "codeHtml" (highlightStart ++ (fragments !! x) ++ highlightEnd) s) [0..(length section) - 1] -- Once all of the code is finished highlighting, we can generate the HTML file -- and write out the documentation. Pass the completed sections into the template -- found in `resources/hyakko.html` generateHTML :: FilePath -> [Map String String] -> IO () generateHTML src section = do let title = takeFileName src dest = destination src source <- sources html <- hyakkoTemplate $ concat [ [("title", title)], if length source > 1 then [("multi","1")] else [], map (\x -> ("source", unlines [ "", " "++takeFileName x,""])) source, map (\x -> ("section", unlines [ "
"
highlightEnd = "