lit
attempts to build upon the evolution of prior literate programming (LP) tools. lit
is language independent with only 2 control structures. lit
uses markdown for styling documents, and relies on indentation for defining code chunks. Learn the syntax, follow a tutorial or continue to read lit
applied to its own source.
A high-level overview of lit.hs
:
<< * >>=
<< imports >>
<< types >>
<< usage messages >>
<< main >>
lit
is a command line utility which relies upon several libraries which handle standard command line use. lit
imports Process
which is the glue for the programs other components. Poll
is the module which provides the "--watch" option, automatically running lit
when the underlying files change.
<< imports >>=
module Main where
import System.Console.GetOpt
import System.Environment
import System.Directory (doesDirectoryExist)
import System.IO
import System.Exit
import Control.Applicative
import Process
import Poll
types
defines the supported command line options and functions used to handle these options
<< types >>=
<< datatype for passing options >>
<< default options >>
<< option transforms >>
Options
serves to represent the different command line options which are handled during use.
<< datatype for passing options >>=
data Options = Options { optCodeDir :: String
, optDocsDir :: String
, optCss :: Maybe String
, optCode :: Bool
, optHtml :: Bool
, optMarkdown :: Bool
, optWatch :: Bool
}
<< default options >>=
startOptions :: Options
startOptions = Options { optCodeDir = "./"
, optDocsDir = "./"
, optCss = Nothing
, optCode = False
, optHtml = False
, optMarkdown = False
, optWatch = False
}
options
is a list of monadic functions, which are applied to the options passed as arguments to lit
. options
provides the general program flow for argument handling.
<< option transforms >>=
options :: [ OptDescr (Options -> IO Options) ]
options =
[ Option "h" ["html"]
(NoArg (\opt -> return opt { optHtml = True }))
"Generate html"
, Option "m" ["markdown"]
(NoArg (\opt -> return opt { optMarkdown = True }))
"Generate markdown"
, Option "c" ["code"]
(NoArg (\opt -> return opt { optCode = True }))
"Generate code by file extension"
, Option "" ["css"]
(ReqArg
(\arg opt -> return opt { optCss = Just arg })
"FILE")
"Specify a css file for html generation"
, Option "" ["docs-dir"]
(ReqArg
(\arg opt -> return opt { optDocsDir = arg })
"DIR")
"Directory for generated docs"
, Option "" ["code-dir"]
(ReqArg
(\arg opt -> return opt { optCodeDir = arg })
"DIR")
"Directory for generated code"
, Option "w" ["watch"]
(NoArg
(\opt -> return opt { optWatch = True}))
"Watch for file changes, automatically run lit"
, Option "v" ["version"]
(NoArg
(\_ -> do
hPutStrLn stderr "Version 0.1.0.9"
exitWith ExitSuccess))
"Print version"
, Option "" ["help"]
(NoArg
(\_ -> do
prg <- getProgName
hPutStrLn stderr (usageInfo usage options)
exitWith ExitSuccess))
"Display help"
]
usage
and help
are definitions used whenever --help
is passed, or an incorrect option is passed.
<< usage messages >>=
usage = "Usage: lit OPTIONS... FILES..."
help = "Try: lit --help"
main
defines the entry point for execution. getOpt
interprets the command line arguments to yield actions
, files
, and errors
<< main >>=
main = do
args <- getArgs
-- Parse options, getting a list of option actions
let (actions, files, errors) = getOpt Permute options args
After interpreting the arguments, foldl
threads the default options through each action, updating the defaults.
<< main >>=
opts <- foldl (>>=) (return startOptions) actions
let Options { optCodeDir = codeDir
, optDocsDir = docsDir
, optMarkdown = markdown
, optCode = code
, optHtml = html
, optCss = mCss
, optWatch = watching
} = opts
A brief check to ensure that the directories actually exist for the given strings. This prevents ambiguous errors about writing to invalid paths.
<< main >>=
codeDirCheck <- doesDirectoryExist codeDir
docsDirCheck <- doesDirectoryExist docsDir
Based on the conditions, we prepare part of the pipeline (the rest occurs in Process.hs) for generating (html/markdown/code). This block aims to gather all the pipes and program errors necessary for determining whether to process or exit. maybeWatch
determines whether the files should be directly passed through the pipes via mapM_
or whether Poll.watch
should manage the pipelines whenever the underlying files change.
<< main >>=
let htmlPipe = if html then [Process.htmlPipeline docsDir mCss] else []
mdPipe = if markdown then [Process.mdPipeline docsDir mCss] else []
codePipe = if code then [Process.codePipeline codeDir mCss] else []
pipes = htmlPipe ++ mdPipe ++ codePipe
maybeWatch = if watching then Poll.watch else mapM_
errors' = if codeDirCheck then [] else ["Directory: " ++ codeDir ++ " does not exist\n"]
errors'' = if docsDirCheck then [] else ["Directory: " ++ docsDir ++ " does not exist\n"]
allErr = errors ++ errors' ++ errors''
In the final call of main, either the programs prints errors or redirects the program flow with maybeWatch
. The bulk of the file processing is passed to the Process.hs
module.
<< main >>=
if allErr /= [] || (not html && not code && not markdown) || files == []
then hPutStrLn stderr ((concat allErr) ++ help)
else (maybeWatch (Process.process pipes)) files