packages feed

hyakko (empty) → 0.2.0

raw patch · 6 files changed

+540/−0 lines, 6 filesdep +basedep +containersdep +directorysetup-changed

Dependencies added: base, containers, directory, filepath, ghc, pandoc, process, regex-compat, regex-pcre

Files

+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2011 Jeremy Hull++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to+deal in the Software without restriction, including without limitation the+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or+sell copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS+IN THE SOFTWARE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ hyakko.cabal view
@@ -0,0 +1,43 @@+name:             hyakko+version:          0.2.0+cabal-version:    >= 1.6+build-type:       Simple+license:          MIT+license-file:     LICENSE+copyright:        (c) 2011 Jeremy Hull+author:           Jeremy Hull <sourdrums@gmail.com>+maintainer:       Jeremy Hull <sourdrums@gmail.com>+bug-reports:      https://github.com/sourrust/hyakko/issues+stability:        alpha+homepage:         http://sourrust.github.com/hyakko/+category:         Documentation+tested-with:      GHC == 7.0.3+synopsis:         Literate-style Documentation Generator+description:      Hyakko is a Haskell port of Docco: the original quick-and-dirty,+                  hundred-line-long, literate-programming-style documentation+                  generator.+data-files:+                  -- HTML template and CSS sytle+                  resources/hyakko.html+                  resources/hyakko.css++source-repository head+  type:           git+  location:       git://github.com/sourrust/hyakko.git++Flag threaded+  default:        True++Executable hyakko+  build-depends:  base >= 4 && < 5,+                  ghc >= 7,+                  process >= 1,+                  filepath >= 1.2,+                  regex-compat >= 0.9,+                  regex-pcre >= 0.9,+                  containers >= 0.4,+                  directory >= 1,+                  pandoc >= 1.8.2+  hs-source-dirs: src+  ghc-options:    -O2 -Wall+  main-is:        Hyakko.hs
+ resources/hyakko.css view
@@ -0,0 +1,186 @@+/*--------------------- Layout and Typography ----------------------------*/+body {+  font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;+  font-size: 15px;+  line-height: 22px;+  color: #252519;+  margin: 0; padding: 0;+}+a {+  color: #261a3b;+}+  a:visited {+    color: #261a3b;+  }+p {+  margin: 0 0 15px 0;+}+h1, h2, h3, h4, h5, h6 {+  margin: 0px 0 15px 0;+}+  h1 {+    margin-top: 40px;+  }+#container {+  position: relative;+}+#background {+  position: fixed;+  top: 0; left: 525px; right: 0; bottom: 0;+  background: #f5f5ff;+  border-left: 1px solid #e5e5ee;+  z-index: -1;+}+#jump_to, #jump_page {+  background: white;+  -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;+  -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;+  font: 10px Arial;+  text-transform: uppercase;+  cursor: pointer;+  text-align: right;+}+#jump_to, #jump_wrapper {+  position: fixed;+  right: 0; top: 0;+  padding: 5px 10px;+}+  #jump_wrapper {+    padding: 0;+    display: none;+  }+    #jump_to:hover #jump_wrapper {+      display: block;+    }+    #jump_page {+      padding: 5px 0 3px;+      margin: 0 0 25px 25px;+    }+      #jump_page .source {+        display: block;+        padding: 5px 10px;+        text-decoration: none;+        border-top: 1px solid #eee;+      }+        #jump_page .source:hover {+          background: #f5f5ff;+        }+        #jump_page .source:first-child {+        }+table td {+  border: 0;+  outline: 0;+}+  td.docs, th.docs {+    max-width: 450px;+    min-width: 450px;+    min-height: 5px;+    padding: 10px 25px 1px 50px;+    overflow-x: hidden;+    vertical-align: top;+    text-align: left;+  }+    .docs pre {+      margin: 15px 0 15px;+      padding-left: 15px;+    }+    .docs p tt, .docs p code {+      background: #f8f8ff;+      border: 1px solid #dedede;+      font-size: 12px;+      padding: 0 0.2em;+    }+    .pilwrap {+      position: relative;+    }+      .pilcrow {+        font: 12px Arial;+        text-decoration: none;+        color: #454545;+        position: absolute;+        top: 3px; left: -20px;+        padding: 1px 2px;+        opacity: 0;+        -webkit-transition: opacity 0.2s linear;+      }+        td.docs:hover .pilcrow {+          opacity: 1;+        }+  td.code, th.code {+    padding: 14px 15px 16px 25px;+    width: 100%;+    vertical-align: top;+    background: #f5f5ff;+    border-left: 1px solid #e5e5ee;+  }+    pre, tt, code {+      font-size: 12px; line-height: 18px;+      font-family: Monaco, Consolas, "Lucida Console", monospace;+      margin: 0; padding: 0;+    }+++/*---------------------- Syntax Highlighting -----------------------------*/+td.linenos { background-color: #f0f0f0; padding-right: 10px; }+span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }+body .hll { background-color: #ffffcc }+body .c { color: #408080; font-style: italic }  /* Comment */+body .err { border: 1px solid #FF0000 }         /* Error */+body .k { color: #954121 }                      /* Keyword */+body .o { color: #666666 }                      /* Operator */+body .cm { color: #408080; font-style: italic } /* Comment.Multiline */+body .cp { color: #BC7A00 }                     /* Comment.Preproc */+body .c1 { color: #408080; font-style: italic } /* Comment.Single */+body .cs { color: #408080; font-style: italic } /* Comment.Special */+body .gd { color: #A00000 }                     /* Generic.Deleted */+body .ge { font-style: italic }                 /* Generic.Emph */+body .gr { color: #FF0000 }                     /* Generic.Error */+body .gh { color: #000080; font-weight: bold }  /* Generic.Heading */+body .gi { color: #00A000 }                     /* Generic.Inserted */+body .go { color: #808080 }                     /* Generic.Output */+body .gp { color: #000080; font-weight: bold }  /* Generic.Prompt */+body .gs { font-weight: bold }                  /* Generic.Strong */+body .gu { color: #800080; font-weight: bold }  /* Generic.Subheading */+body .gt { color: #0040D0 }                     /* Generic.Traceback */+body .kc { color: #954121 }                     /* Keyword.Constant */+body .kd { color: #954121; font-weight: bold }  /* Keyword.Declaration */+body .kn { color: #954121; font-weight: bold }  /* Keyword.Namespace */+body .kp { color: #954121 }                     /* Keyword.Pseudo */+body .kr { color: #954121; font-weight: bold }  /* Keyword.Reserved */+body .kt { color: #B00040 }                     /* Keyword.Type */+body .m { color: #666666 }                      /* Literal.Number */+body .s { color: #219161 }                      /* Literal.String */+body .na { color: #7D9029 }                     /* Name.Attribute */+body .nb { color: #954121 }                     /* Name.Builtin */+body .nc { color: #0000FF; font-weight: bold }  /* Name.Class */+body .no { color: #880000 }                     /* Name.Constant */+body .nd { color: #AA22FF }                     /* Name.Decorator */+body .ni { color: #999999; font-weight: bold }  /* Name.Entity */+body .ne { color: #D2413A; font-weight: bold }  /* Name.Exception */+body .nf { color: #0000FF }                     /* Name.Function */+body .nl { color: #A0A000 }                     /* Name.Label */+body .nn { color: #0000FF; font-weight: bold }  /* Name.Namespace */+body .nt { color: #954121; font-weight: bold }  /* Name.Tag */+body .nv { color: #19469D }                     /* Name.Variable */+body .ow { color: #AA22FF; font-weight: bold }  /* Operator.Word */+body .w { color: #bbbbbb }                      /* Text.Whitespace */+body .mf { color: #666666 }                     /* Literal.Number.Float */+body .mh { color: #666666 }                     /* Literal.Number.Hex */+body .mi { color: #666666 }                     /* Literal.Number.Integer */+body .mo { color: #666666 }                     /* Literal.Number.Oct */+body .sb { color: #219161 }                     /* Literal.String.Backtick */+body .sc { color: #219161 }                     /* Literal.String.Char */+body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */+body .s2 { color: #219161 }                     /* Literal.String.Double */+body .se { color: #BB6622; font-weight: bold }  /* Literal.String.Escape */+body .sh { color: #219161 }                     /* Literal.String.Heredoc */+body .si { color: #BB6688; font-weight: bold }  /* Literal.String.Interpol */+body .sx { color: #954121 }                     /* Literal.String.Other */+body .sr { color: #BB6688 }                     /* Literal.String.Regex */+body .s1 { color: #219161 }                     /* Literal.String.Single */+body .ss { color: #19469D }                     /* Literal.String.Symbol */+body .bp { color: #954121 }                     /* Name.Builtin.Pseudo */+body .vc { color: #19469D }                     /* Name.Variable.Class */+body .vg { color: #19469D }                     /* Name.Variable.Global */+body .vi { color: #19469D }                     /* Name.Variable.Instance */+body .il { color: #666666 }                     /* Literal.Number.Integer.Long */
+ resources/hyakko.html view
@@ -0,0 +1,44 @@+<!DOCTYPE html>++<html>+<head>+  <title>$title$</title>+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">+  <link rel="stylesheet" media="all" href="hyakko.css" />+</head>+<body>+  <div id="container">+    <div id="background"></div>+    $if(multi)$+      <div id="jump_to">+        Jump To &hellip;+        <div id="jump_wrapper">+          <div id="jump_page">+$for(source)$+$source$+$endfor$+          </div>+        </div>+      </div>+    $endif$+    <table cellpadding="0" cellspacing="0">+      <thead>+        <tr>+          <th class="docs">+            <h1>+              $title$+            </h1>+          </th>+          <th class="code">+          </th>+        </tr>+      </thead>+      <tbody>+$for(section)$+$section$+$endfor$+      </tbody>+    </table>+  </div>+</body>+</html>
+ src/Hyakko.hs view
@@ -0,0 +1,245 @@+-- **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 [+      "<a class='source' href='"++(takeFileName $ destination x)++"'>",+      "  "++takeFileName x,"</a>"])) source,+    map (\x -> ("section", unlines [+      "<tr id='section-"++show (x + 1)++"'>",+      "  <td class='docs'>",+      "    <div class='pilwrap'>",+      "      <a class='pilcrow' href='#section-"++show (x + 1)++"'>&#955;</a>",+      "    </div>",+      (section !! x) M.! "docsHtml",+      "  </td>",+      "  <td class='code'>",+      (section !! x) M.! "codeHtml",+      "  </td>",+      "</tr>" ])) [0..(length section) - 1]+    ]+  putStrLn $ "hyakko: " ++ src ++ " -> " ++ dest+  writeFile dest html++-- ### Helpers & Setup++-- A list of the languages that Hyakko supports, mapping the file extension to+-- the name of the Pygments lexer and the symbol that indicates a comment. To+-- add another language to Hyakko's repertoire, add it here.+languages :: Map String (Map String String)+languages =+  let hashSymbol = ("symbol", "#")+      l = M.fromList [+          (".hs", M.fromList [+            ("name", "haskell"), ("symbol", "--")]),+          (".coffee", M.fromList [+            ("name", "coffee-script"), hashSymbol]),+          (".js", M.fromList [+            ("name", "javascript"), ("symbol", "//")]),+          (".py", M.fromList [+            ("name", "python"), hashSymbol]),+          (".rb", M.fromList [+            ("name", "ruby"), hashSymbol])+          ]+  -- Build out the appropriate matchers and delimiters for each language.+  in M.map (\x -> let s = x M.! "symbol"+    -- Does the line begin with a comment?+    in M.insert "comment" ("^\\s*"++s++"\\s?") $+       -- The dividing token we feed into Pygments, to delimit the boundaries+       -- between sections.+       M.insert "dividerText" ("\n"++s++"DIVIDER\n") $+       -- The mirror of `divider_text` that we expect Pygments to return. We can+       -- split on this to recover the original sections.+       -- Note: the class is "c" for Python and "c1" for the other languages+       M.insert "dividerHtml" ("\n*<span class=\"c1?\">"++s++"DIVIDER</span>\n") x) l++-- Get the current language we're documenting, based on the extension.+getLanguage :: FilePath -> Maybe (Map String String)+getLanguage src = M.lookup (takeExtension src) languages++-- Compute the destination HTML path for an input source file path. If the source+-- is `lib/example.hs`, the HTML will be at docs/example.html+destination :: FilePath -> FilePath+destination fp = "docs" </> (takeBaseName fp) ++ ".html"++-- Ensure that the destination directory exists.+ensureDirectory :: Callback -> IO ()+ensureDirectory cb = system "mkdir -p docs" >> cb++-- Create the template that we will use to generate the Hyakko HTML page.+hyakkoTemplate :: [(String, String)] -> IO String+hyakkoTemplate var = readDataFile "resources/hyakko.html" >>=+  return . renderTemplate var++-- The CSS styles we'd like to apply to the documentation.+hyakkoStyles :: IO String+hyakkoStyles = readDataFile "resources/hyakko.css"++-- The start and end of each Pygments highlight block.+highlightStart, highlightEnd, highlightReplace :: String+highlightStart   = "<div class=\"highlight\"><pre>"+highlightEnd     = "</pre></div>"+highlightReplace = highlightStart ++ "|" ++ highlightEnd++-- Reads from resource path given in cabal package+readDataFile :: FilePath -> IO String+readDataFile f = getDataFileName f >>= readFile++-- For each source file passed in as an argument, generate the documentation.+sources :: IO [FilePath]+sources = getArgs >>= unpack >>= return . sort . concat+  where+    unpack = mapM (\x -> do+      isDir <- doesDirectoryExist x+      if isDir then unpackDirectories x else return [x])++-- Turns the directory give into a list of files including all of the files+-- in sub-directories.+unpackDirectories :: FilePath -> IO [FilePath]+unpackDirectories d = do+  content <- getDirectoryContents d >>= return . filter (=~ "[^\\.{1,2}]")+  let content' = map (d </>) content+  files <- filterM doesFileExist content'+  subdir <- filterM doesDirectoryExist content'+  subcontent <- mapM unpackDirectories subdir >>= return . concat+  return (files ++ subcontent)++-- Run the script.+main :: IO ()+main = do+  style <- hyakkoStyles+  source <- sources+  ensureDirectory $ do+    writeFile "docs/hyakko.css" style+    generateDocumentation source