@@ -10,15 +10,16 @@ module Text.Hakyll.Render
13−import Text.Template hiding (render)
14−import qualified Data.ByteString.Lazy.Char8 as B
15import qualified Data.Map as M
13import qualified Data.Map as M
14+import Data.List (isPrefixOf)
16import Control.Monad (unless, liftM, foldM)
15import Control.Monad (unless, liftM, foldM)
16+import Data.Char (isAlpha)
17+import Data.Maybe (fromMaybe)
18import System.Directory (copyFile)
19import System.Directory (copyFile)
19import System.IO
20import System.IO
21−import Text.Hakyll.Context (ContextManipulation)
22+import Text.Hakyll.Context (Context, ContextManipulation)
22import Text.Hakyll.Page
23import Text.Hakyll.Page
23import Text.Hakyll.Renderable
24import Text.Hakyll.Renderable
24import Text.Hakyll.File
25import Text.Hakyll.File
@@ -33,6 +34,19 @@ depends file dependencies action = do
33 valid <- isCacheValid (toDestination file) dependencies
34 valid <- isCacheValid (toDestination file) dependencies
34 unless valid action
35 unless valid action
37+-- | Substitutes `$identifiers` in the given string by values from the given
38+-- "Context". When a key is not found, it is left as it is.
39+substitute :: String -> Context -> String
41+substitute string context
42+ | "$$" `isPrefixOf` string = "$$" ++ (substitute (tail tail') context)
43+ | "$" `isPrefixOf` string = substitute'
44+ | otherwise = (head string) : (substitute tail' context)
45+ where tail' = tail string
46+ (key, rest) = break (not . isAlpha) tail'
47+ replacement = fromMaybe ('$' : key) $ M.lookup key context
48+ substitute' = replacement ++ substitute rest context
36-- | Render to a Page.
50-- | Render to a Page.
37render :: Renderable a
51render :: Renderable a
38 => FilePath -- ^ Template to use for rendering.
52 => FilePath -- ^ Template to use for rendering.
@@ -49,18 +63,18 @@ renderWith :: Renderable a
49 -> IO Page -- ^ The body of the result will contain the render.
63 -> IO Page -- ^ The body of the result will contain the render.
50renderWith manipulation templatePath renderable = do
64renderWith manipulation templatePath renderable = do
51 handle <- openFile templatePath ReadMode
65 handle <- openFile templatePath ReadMode
52− templateString <- liftM B.pack $ hGetContents handle
66+ templateString <- hGetContents handle
53 seq templateString $ hClose handle
67 seq templateString $ hClose handle
54 context <- liftM manipulation $ toContext renderable
68 context <- liftM manipulation $ toContext renderable
55 -- Ignore $root when substituting here. We will only replace that in the
69 -- Ignore $root when substituting here. We will only replace that in the
56 -- final render (just before writing).
70 -- final render (just before writing).
57− let contextIgnoringRoot = M.insert (B.pack "root") (B.pack "$root") context
71+ let contextIgnoringRoot = M.insert "root" "$root" context
58 body = substitute templateString contextIgnoringRoot
72 body = substitute templateString contextIgnoringRoot
59− return $ fromContext (M.insert (B.pack "body") body context)
73+ return $ fromContext (M.insert "body" body context)
61-- | Render each renderable with the given template, then concatenate the
75-- | Render each renderable with the given template, then concatenate the
63−renderAndConcat :: Renderable a => FilePath -> [a] -> IO B.ByteString
77+renderAndConcat :: Renderable a => FilePath -> [a] -> IO String
64renderAndConcat = renderAndConcatWith id
78renderAndConcat = renderAndConcatWith id
66-- | Render each renderable with the given template, then concatenate the
80-- | Render each renderable with the given template, then concatenate the
@@ -70,14 +84,14 @@ renderAndConcatWith :: Renderable a
70 => ContextManipulation
84 => ContextManipulation
71 -> FilePath
85 -> FilePath
73− -> IO B.ByteString
87+ -> IO String
74renderAndConcatWith manipulation templatePath renderables =
88renderAndConcatWith manipulation templatePath renderables =
75− foldM concatRender' B.empty renderables
89+ foldM concatRender' [] renderables
76− where concatRender' :: Renderable a => B.ByteString -> a -> IO B.ByteString
90+ where concatRender' :: Renderable a => String -> a -> IO String
77 concatRender' chunk renderable = do
91 concatRender' chunk renderable = do
78 rendered <- renderWith manipulation templatePath renderable
92 rendered <- renderWith manipulation templatePath renderable
79 let body = getBody rendered
93 let body = getBody rendered
80− return $ B.append chunk $ body
94+ return $ chunk ++ body
82-- | Chain a render action for a page with a number of templates. This will
96-- | Chain a render action for a page with a number of templates. This will
83-- also write the result to the site destination. This is the preferred way
97-- also write the result to the site destination. This is the preferred way
@@ -100,11 +114,11 @@ writePage :: Page -> IO ()
100writePage page = do
114writePage page = do
101 let destination = toDestination url
115 let destination = toDestination url
102 makeDirectories destination
116 makeDirectories destination
103− B.writeFile destination body
117+ writeFile destination body
104 where url = getURL page
118 where url = getURL page
105− -- Substitute $root here, just before writing.
119+ -- Substitute $root here, just before writing.
106− body = substitute (getBody page)
120+ body = substitute (getBody page)
107− (M.singleton (B.pack "root") (B.pack $ toRoot url))
121+ (M.singleton "root" $ toRoot url)
109-- | Mark a certain file as static, so it will just be copied when the site is
123-- | Mark a certain file as static, so it will just be copied when the site is
110-- generated.
124-- generated.