[automated table of contents generation. added other filters section to routing filters Jeremy Shaw **20100527204818 Ignore-this: b20f19348661c58453a5aecd47b510a3 ] hunk ./Dir.lhs 7 -

Using dir to match on static path components

+

Using dir to match on static path components

hunk ./Dir2.lhs 7 -

Using dir to match on multiple components

+

Using dir to match on multiple components

hunk ./Dirs.lhs 7 -

Using dirs as shorthand to match on multiple components

+

Using dirs as shorthand to match on multiple components

hunk ./Main.lhs 17 -

Happstack Crashcourse

+

Happstack Crashcourse

hunk ./Main.lhs 36 -
    -
  1. Hello World!
  2. -
  3. Routing URLS -
      -
    1. Variable Path Segments -
        -
      1. path
      2. -
      3. FromReqURI: extending path
      4. -
      -
    2. -
    -
  4. -
+#include "toc.html" hunk ./Makefile 18 +toc.html: $(DEPS) gen-toc.hs + runhaskell gen-toc.hs > toc.html + hunk ./Makefile 41 -Main.html : $(SRC) $(EXTRA_DEPS) +Main.html : $(SRC) $(EXTRA_DEPS) toc.html hunk ./MonadPlus.lhs 7 -

Choosing between multiple ServerPartTs

+

Choosing between multiple ServerPartTs

hunk ./RouteFilters.lhs 16 -

Route Filters

+

Route Filters

hunk ./RouteFilters.lhs 30 +

Other Routing Filters

+ +

SimpleHTTP includes a number of other useful routing filters, such as:

+
+
nullDir :: (ServerMonad m, MonadPlus m) => m ()
+
check that there are no unmatched path segments remaining
+ +
host :: (ServerMonad m, MonadPlus m) => String -> m a -> m a
+
match on a specific host name in the Request
+ +
withHost :: (ServerMonad m, MonadPlus m) => (String -> m a) -> m a
+
Lookup the host header and pass it to the handler.
+ +
uriRest :: (ServerMonad m) => (String -> m a) -> m a
+
Grab the rest of the URL (dirs + query) and passes it to your handler
+ +
anyPath :: (ServerMonad m, MonadPlus m) => m r -> m r
+
Pop any path element and ignore when choosing a 'ServerPartT' to handle the request.
+ +
trailingSlash :: (ServerMonad m, MonadPlus m) => m ()
+
Guard which checks that the Request URI ends in /. Useful for distinguishing between foo and foo/
+
addfile ./gen-toc.hs hunk ./gen-toc.hs 1 +module Main where + +import Data.List (groupBy) +import Data.Function (on) +-- import Data.Tree +import Text.HTML.TagSoup +import Text.Html + +isOpenHeader t = + t ~== "

" || t ~== "

" || t ~== "

" || t ~== "

" || t ~== "

" + +isCloseHeader t = + t ~== "
" || t ~== "" || t ~== "" || t ~== "" || t ~== "" + +data Header = Header { level :: Int + , aname :: Maybe String + , label :: String + , file :: FilePath + } deriving (Eq, Ord, Read, Show) + +extractHeader :: FilePath -> [Tag String] -> Header +extractHeader fp (TagOpen hStr _:ts) = + let lvl = case hStr of + "h1" -> 1 + "h2" -> 2 + "h3" -> 3 + "h4" -> 4 + "h5" -> 5 + in extractName lvl ts + where + extractName lvl (TagOpen "a" attrs: ts) = + case lookup "name" attrs of + Nothing -> extractLbl lvl Nothing ts + (Just n) -> extractLbl lvl (Just n) ts + extractName lvl ts = extractLbl lvl Nothing ts + extractLbl lvl n ts = Header lvl n (innerText ts) fp + +getHeaders :: FilePath -> IO [Header] +getHeaders fp = + do c <- fmap parseTags $ readFile fp + let headers = map (extractHeader fp . takeWhile (\t -> not (isCloseHeader t))) $ sections isOpenHeader c + return headers + +files = + [ "HelloWorld.html" + , "RouteFilters.html" + ] + + +main :: IO () +main = + do hs <- fmap concat $ mapM getHeaders files + let htree = map snd $ toTree (map headerToPair hs) + print (forestToHtml htree) + +forestToHtml :: [Tree Header] -> Html +forestToHtml [] = noHtml +forestToHtml trees = olist $ concatHtml (map treeToHtml trees) + +treeToHtml :: Tree Header -> Html +treeToHtml (Tree header children) = li $ headerToHtml header +++ forestToHtml children + +headerToHtml :: Header -> Html +headerToHtml h = (anchor (toHtml (label h))) ! [href ((file h) ++ (maybe "" ("#"++) (aname h))) ] + +headerToPair :: Header -> (Int, Header) +headerToPair h = (level h, h) + + +data Tree a = Tree a [Tree a] deriving Show + +toTree :: Show a => [(Int, a)] -> [(Int, Tree a)] +toTree pairs = + f [] (reverse pairs) + where + f :: Show a => [(Int, Tree a)] -> [(Int, a)] -> [(Int, Tree a)] + f [] ((d', x) : more) = f [(d', Tree x [])] more + f stack [] = stack + f stack@((d, trees) : _) pairs@((d', x) : more) = + case compare d (d' + 1) of + -- If the new pair depth is one less than the stack top depth, pair becomes new parent + EQ -> let (children, other) = break (\ (d, _) -> d /= d' + 1) stack in f ((d', Tree x (map snd children)) : other) more + -- If the new pair depth is less than or equal to the stack top depth, push it + LT -> f ((d', Tree x []) : stack) more + -- If the new pair depth is greater than the stack top depth, it is an error. + GT -> error $ "Invalid traversal: stack=" ++ show stack ++ ", pairs=" ++ show pairs + +{- +toForest :: Int -> [Header] -> Forest Header +toForest _ [] = [] +toForest currLvl hs@(h@(Header lvl _ _) : rest) | +-} + + + hunk ./theme.css 3 - font-family: san-serif; + font-family: sans-serif;