on master help sign in
Working trimming but module loop and formatting req.
ca7b78esamgd
hakyll.cabal+1−1
@@ -117,7 +117,7 @@ Library
117 Hakyll.Web.Paginate
117 Hakyll.Web.Paginate
118 Hakyll.Web.Template
118 Hakyll.Web.Template
119 Hakyll.Web.Template.Internal
119 Hakyll.Web.Template.Internal
120− Hakyll.Web.Template.Canonicalize
120+ Hakyll.Web.Template.Trim
121 Hakyll.Web.Template.Context
121 Hakyll.Web.Template.Context
122 Hakyll.Web.Template.List
122 Hakyll.Web.Template.List
123
123
src/Hakyll/Web/Template/Canonicalize.hsdeleted−86
@@ -1,86 +0,0 @@
1−--------------------------------------------------------------------------------
2−-- | TODO
3−module Hakyll.Web.Template.Canonicalize
4− ( canonicalize
5− ) where
6−
7−
8−--------------------------------------------------------------------------------
9−import Hakyll.Web.Template.Internal
10−
11−
12−--------------------------------------------------------------------------------
13−--
14−-- Some initial implementation notes. Note: Not valid syntax etc.
15−--
16−--
17−-- Top level ONLY:
18−-- [TrimL, t, TrimR] = [t]
19−--
20−-- Dedupe:
21−--
22−-- List:
23−--
24−-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
25−--
26−-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
27−--
28−-- If:
29−--
30−-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
31−--
32−-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
33−--
34−-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
35−--
36−-- For:
37−--
38−-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
39−--
40−-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
41−--
42−-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
43−--
44−--
45−-- Sink:
46−--
47−-- If:
48−--
49−-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
50−--
51−-- [t1, If ex t e, TrimL, t2] = if isJust e
52−-- then [t1, If ex t [e, TrimL], t2]
53−-- else [t1, If ex [t, TrimL] e, t2]
54−--
55−-- For:
56−--
57−-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
58−--
59−-- [t1, For e b sep, TrimL, t2] = if isJust sep
60−-- then [t1, For e b [sep, TrimL], t2]
61−-- else [t1, For e [b, TrimL] sep, t2]
62−--
63−--
64−-- Shift/Lift:
65−--
66−-- If:
67−--
68−-- If ex [t1, TrimR] (Just e) = If ex t1 [TrimR, e]
69−--
70−-- If ex [t1, TrimR] Nothing = [If ex t1 Nothing, TrimR]
71−--
72−-- If ex t [TrimL, e] = If ex [t, TrimL] e
73−--
74−--
75−-- For:
76−--
77−-- For e [t1, TrimR] (Just sep) = For e t1 [TrimR, sep]
78−--
79−-- For e [t1, TrimR] Nothing = For e t1 [TrimR, sep]
80−--
81−-- For e b [TrimL, sep] = For e [b, TrimL] sep
82−--
83−--
84−--
85−canonicalize :: Template -> Template
86−canonicalize = undefined
src/Hakyll/Web/Template/Internal.hs+74−38
@@ -129,6 +129,20 @@ instance Binary TemplateExpr where
129 _ -> error "Hakyll.Web.Template.Internal: Error reading cached template"
129 _ -> error "Hakyll.Web.Template.Internal: Error reading cached template"
130
130
131
131
132+--------------------------------------------------------------------------------
133+(.~) :: [TemplateElement] -> Template -> Template
134+ts .~ (Template t) = Template (ts ++ t)
135+
136+infixr 6 .~
137+
138+
139+--------------------------------------------------------------------------------
140+(~.) :: Template -> [TemplateElement] -> Template
141+(Template t) ~. ts = Template (t ++ ts)
142+
143+infixl 5 ~.
144+
145+
132--------------------------------------------------------------------------------
146--------------------------------------------------------------------------------
133readTemplate :: String -> Template
147readTemplate :: String -> Template
134readTemplate input = case P.parse template "" input of
148readTemplate input = case P.parse template "" input of
@@ -159,10 +173,7 @@ expr = P.try $ do
159 trimLExpr <- trimOpen
173 trimLExpr <- trimOpen
160 e <- expr'
174 e <- expr'
161 trimRExpr <- trimClose
175 trimRExpr <- trimClose
162− return $ Template $ mconcat [ [TrimL | trimLExpr]
176+ return $ [TrimL | trimLExpr] .~ Template [Expr e] ~. [TrimR | trimRExpr]
163− , [Expr e]
164− , [TrimR | trimRExpr]
165− ]
166
177
167
178
168--------------------------------------------------------------------------------
179--------------------------------------------------------------------------------
@@ -194,73 +205,87 @@ trimClose = do
194--------------------------------------------------------------------------------
205--------------------------------------------------------------------------------
195conditional :: P.Parser Template
206conditional :: P.Parser Template
196conditional = P.try $ do
207conditional = P.try $ do
208+ -- if
197 trimLIf <- trimOpen
209 trimLIf <- trimOpen
198 void $ P.string "if("
210 void $ P.string "if("
199 e <- expr'
211 e <- expr'
200 void $ P.char ')'
212 void $ P.char ')'
201 trimRIf <- trimClose
213 trimRIf <- trimClose
202−
214+ -- then
203 thenBranch <- template
215 thenBranch <- template
204−
216+ -- else
205− elseBranch <- P.optionMaybe $ P.try $ do
217+ elseParse <- opt "else"
206− trimLElse <- trimOpen
218+ -- endif
207− void $ P.string "else"
208− trimRElse <- trimClose
209− elseBody <- template
210− pure $ mconcat $ concat [ [Template [TrimL] | trimLElse]
211− , [Template [TrimR] | trimRElse]
212− , [elseBody]
213− ]
214−
215 trimLEnd <- trimOpen
219 trimLEnd <- trimOpen
216 void $ P.string "endif"
220 void $ P.string "endif"
217 trimREnd <- trimClose
221 trimREnd <- trimClose
218
222
223+ -- As else is optional we need to sort out where any Trim_s need to go.
224+ let (thenBody, elseBody) = maybe (thenNoElse, Nothing) thenElse elseParse
225+ where thenNoElse =
226+ [TrimR | trimRIf] .~ thenBranch ~. [TrimL | trimLEnd]
227+
228+ thenElse (trimLElse, elseBranch, trimRElse) = (thenB, elseB)
229+ where thenB = [TrimR | trimRIf]
230+ .~ thenBranch
231+ ~. [TrimL | trimLElse]
232+
233+ elseB = Just $ [TrimR | trimRElse]
234+ .~ elseBranch
235+ ~. [TrimL | trimLEnd]
236+
219− pure $ Template $ mconcat [ [TrimL | trimLIf]
237+ pure $ [TrimL | trimLIf]
220− , [TrimR | trimRIf]
238+ .~ Template [If e thenBody elseBody]
221− , [If e thenBranch elseBranch]
222− , [TrimL | trimLEnd]
223− , [TrimR | trimREnd]
239+ ~. [TrimR | trimREnd]
224− ]
225
240
226
241
227--------------------------------------------------------------------------------
242--------------------------------------------------------------------------------
228for :: P.Parser Template
243for :: P.Parser Template
229for = P.try $ do
244for = P.try $ do
245+ -- for
230 trimLFor <- trimOpen
246 trimLFor <- trimOpen
231 void $ P.string "for("
247 void $ P.string "for("
232 e <- expr'
248 e <- expr'
233 void $ P.char ')'
249 void $ P.char ')'
234 trimRFor <- trimClose
250 trimRFor <- trimClose
235−
251+ -- body
236− body <- template
252+ bodyBranch <- template
237− sep <- P.optionMaybe $ P.try (P.string "$sep$") >> template
253+ -- sep
238−
254+ sepParse <- opt "sep"
255+ -- endfor
239 trimLEnd <- trimOpen
256 trimLEnd <- trimOpen
240 void $ P.string "endfor"
257 void $ P.string "endfor"
241 trimREnd <- trimClose
258 trimREnd <- trimClose
242
259
260+ -- As sep is optional we need to sort out where any Trim_s need to go.
261+ let (forBody, sepBody) = maybe (forNoSep, Nothing) forSep sepParse
262+ where forNoSep =
263+ [TrimR | trimRFor] .~ bodyBranch ~. [TrimL | trimLEnd]
264+
265+ forSep (trimLSep, sepBranch, trimRSep) = (forB, sepB)
266+ where forB = [TrimR | trimRFor]
267+ .~ bodyBranch
268+ ~. [TrimL | trimLSep]
269+
270+ sepB = Just $ [TrimR | trimRSep]
271+ .~ sepBranch
272+ ~. [TrimL | trimLEnd]
273+
243− pure $ Template $ mconcat [ [TrimL | trimLFor]
274+ pure $ [TrimL | trimLFor]
244− , [TrimR | trimRFor]
275+ .~ Template [For e forBody sepBody]
245− , [For e body sep]
246− , [TrimL | trimLEnd]
247− , [TrimR | trimREnd]
276+ ~. [TrimR | trimREnd]
248− ]
249
277
250
278
251--------------------------------------------------------------------------------
279--------------------------------------------------------------------------------
252partial :: P.Parser Template
280partial :: P.Parser Template
253partial = P.try $ do
281partial = P.try $ do
254− trimLPartial <- trimOpen
282+ trimLPart <- trimOpen
255 void $ P.string "partial("
283 void $ P.string "partial("
256 e <- expr'
284 e <- expr'
257 void $ P.char ')'
285 void $ P.char ')'
258− trimRPartial <- trimClose
286+ trimRPart <- trimClose
259
287
260− pure $ Template $ mconcat [ [TrimL | trimLPartial]
288+ pure $ [TrimL | trimLPart] .~ Template [Partial e] ~. [TrimR | trimRPart]
261− , [Partial e]
262− , [TrimR | trimRPartial]
263− ]
264
289
265
290
266--------------------------------------------------------------------------------
291--------------------------------------------------------------------------------
@@ -294,3 +319,14 @@ stringLiteral = do
294--------------------------------------------------------------------------------
319--------------------------------------------------------------------------------
295key :: P.Parser TemplateKey
320key :: P.Parser TemplateKey
296key = TemplateKey <$> metadataKey
321key = TemplateKey <$> metadataKey
322+
323+
324+--------------------------------------------------------------------------------
325+opt :: String -> P.Parser (Maybe (Bool, Template, Bool))
326+opt clause = P.optionMaybe $ P.try $ do
327+ trimL <- trimOpen
328+ void $ P.string clause
329+ trimR <- trimClose
330+ branch <- template
331+ pure (trimL, branch, trimR)
332+
src/Hakyll/Web/Template/Trim.hsnew file+178
@@ -0,0 +1,178 @@
1+--------------------------------------------------------------------------------
2+-- | TODO
3+module Hakyll.Web.Template.Internal.Trim
4+ ( trim
5+ ) where
6+
7+
8+--------------------------------------------------------------------------------
9+import Data.Char (isSpace)
10+import Data.List (dropWhileEnd)
11+
12+
13+--------------------------------------------------------------------------------
14+import Hakyll.Web.Template.Internal
15+
16+
17+--------------------------------------------------------------------------------
18+trim :: Template -> Template
19+trim = cleanse . canonicalize
20+
21+
22+--------------------------------------------------------------------------------
23+cleanse :: Template -> Template
24+cleanse = tmap (recurse cleanse . process)
25+ where process [] = []
26+ process (TrimR:Chunk str:ts) = Chunk (lstrip str):process ts
27+ process (Chunk str:TrimL:ts) = Chunk (rstrip str):process ts
28+ process (t:ts) = t:process ts
29+
30+ lstrip = dropWhile isSpace
31+ rstrip = dropWhileEnd isSpace
32+
33+--------------------------------------------------------------------------------
34+--
35+-- Invariant: Every TrimL should have a Chunk to its Left
36+-- Every TrimR should have a Chunk to its Right
37+--
38+--
39+-- Some initial implementation notes. Note: Not valid syntax etc.
40+--
41+--
42+--
43+--
44+--------------------------------------------------------------------------------
45+canonicalize :: Template -> Template
46+canonicalize = go
47+ where go t = let t' = redundant . swap . dedupe $ sink t
48+ in if t == t' then t else go t'
49+
50+
51+--------------------------------------------------------------------------------
52+-- | 'redundant' removes the redundant 'TrimR's and 'TrimL's from the
53+-- 'Template's list of 'TemplateExpr's. It does _not_ recurse down the AST.
54+--
55+-- Note: Should _only_ be used on the top level 'Template'.
56+--
57+redundant :: Template -> Template
58+redundant = tmap (recurse redundant . process)
59+ where -- Remove the leading 'TrimL's.
60+ process (TrimL:ts) = process ts
61+ -- Remove trailing 'TrimR's.
62+ process ts = foldr trailing [] ts
63+ where trailing TrimR [] = []
64+ trailing t ts = t:ts
65+
66+
67+--------------------------------------------------------------------------------
68+-- Commute:
69+--
70+-- List:
71+--
72+-- [t1, TrimR, TrimL, t2] = [t1, TrimL, TrimR, t2]
73+--
74+-- Rest should come for free provided Trim's are Sunk/Shifted etc I think.
75+--
76+swap :: Template -> Template
77+swap = tmap (recurse swap . process)
78+ where process [] = []
79+ process (TrimR:TrimL:ts) = TrimL:process (TrimR:ts)
80+ process (t:ts) = t:process ts
81+
82+
83+--------------------------------------------------------------------------------
84+--
85+-- Dedupe:
86+--
87+-- List:
88+--
89+-- [t1, TrimR, TrimR, t2] = [t1, TrimR, t2]
90+--
91+-- [t1, TrimL, TrimL, t2] = [t1, TrimL, t2]
92+--
93+-- If: Should come for free after Trim_'s have been sunk.
94+--
95+-- [t1, TrimR, If ex [TrimR, t] e, t2] = [t1, If ex [TrimR, t] e, t2]
96+--
97+-- [t1, If ex t [e, TrimL], TrimL, t2] = [t1, If ex t [e, TrimL], t2]
98+--
99+-- [t1, If ex [t, TrimL] Nothing, TrimL, t2] = [t1, If ex [t, TrimL] Nothing, t2]
100+--
101+-- For:
102+--
103+-- [t1, TrimR, For e [TrimR, b] sep, t2] = [t1, For e [TrimR, b] sep, t2]
104+--
105+-- [t1, For e b [sep, TrimL], TrimL, t2] = [t1, For e b [sep, TrimL], t2]
106+--
107+-- [t1, For e [b, TrimL] Nothing, TrimL, t2] = [t1, For e [b, TrimL] Nothing, t2]
108+--
109+dedupe :: Template -> Template
110+dedupe = tmap (recurse dedupe . process)
111+ where process [] = []
112+ process (TrimR:TrimR:ts) = process (TrimR:ts)
113+ process (TrimL:TrimL:ts) = process (TrimL:ts)
114+ process (t:ts) = t:process ts
115+
116+
117+--------------------------------------------------------------------------------
118+--
119+-- Sink:
120+--
121+-- If:
122+--
123+-- [t1, TrimR, If ex t e, t2] = [t1, If ex [TrimR, t] e, t2]
124+--
125+-- [t1, If ex t e, TrimL, t2] = if isJust e
126+-- then [t1, If ex t [e, TrimL], t2]
127+-- else [t1, If ex [t, TrimL] e, t2]
128+--
129+-- For:
130+--
131+-- [t1, TrimR, For e b sep, t2] = [t1, For e [TrimR, b] sep, t2]
132+--
133+-- [t1, For e b sep, TrimL, t2] = if isJust sep
134+-- then [t1, For e b [sep, TrimL], t2]
135+-- else [t1, For e [b, TrimL] sep, t2]
136+--
137+--
138+sink :: Template -> Template
139+sink = tmap (recurse sink . process)
140+ where process [] = []
141+ -- Right sink TrimR into If thenbody.
142+ process (TrimR:If e (Template tb) eb:ts)
143+ = If e (Template (TrimR:tb)) eb:process ts
144+ -- Left sink TrimL into If thenbody.
145+ process (If e (Template tb) Nothing:TrimL:ts)
146+ = If e (Template (tb ++ [TrimL])) Nothing:process ts
147+ -- Left sink TrimL into If elsebody.
148+ process (If e tb (Just (Template eb)):TrimL:ts)
149+ = If e tb (Just (Template (eb ++ [TrimL]))):process ts
150+ -- Right sink TrimR into For body.
151+ process (TrimR:For e (Template b) sep:ts)
152+ = For e (Template (TrimR:b)) sep:process ts
153+ -- Left sink TrimL into For body.
154+ process (For e (Template b) Nothing:TrimL:ts)
155+ = For e (Template (b ++ [TrimL])) Nothing:process ts
156+ -- Left sink TrimL into For sep.
157+ process (For e b (Just (Template sep)):TrimL:ts)
158+ = For e b (Just (Template (sep ++ [TrimL]))):process ts
159+ -- Otherwise move on.
160+ process (t:ts) = t:process ts
161+
162+
163+--------------------------------------------------------------------------------
164+tmap :: ([TemplateElement] -> [TemplateElement]) -> Template -> Template
165+tmap f (Template t) = Template (f t)
166+
167+
168+--------------------------------------------------------------------------------
169+recurse :: (Template -> Template) -> [TemplateElement] -> [TemplateElement]
170+recurse f [] = []
171+recurse f (x:xs) = process x:recurse f xs
172+ where process x = case x of
173+ If e tb eb -> If e (f tb) (f <$> eb)
174+ For e t s -> For e (f t) (f <$> s)
175+ _ -> x
176+
177+
178+--------------------------------------------------------------------------------
tests/Hakyll/Web/Template/Tests.hs+8−8
@@ -41,30 +41,30 @@ tests = testGroup "Hakyll.Core.Template.Tests" $ concat
41 -- 'If' trim check.
41 -- 'If' trim check.
42 , Template
42 , Template
43 [ TrimL
43 [ TrimL
44− , TrimR
45 , If (Ident (TemplateKey "body"))
44 , If (Ident (TemplateKey "body"))
45+ (Template [ TrimR
46− (Template [ Chunk "\n"
46+ , Chunk "\n"
47 , Expr (Ident (TemplateKey "body"))
47 , Expr (Ident (TemplateKey "body"))
48 , Chunk "\n"
48 , Chunk "\n"
49+ , TrimL
49 ])
50 ])
50− (Just (Template [ TrimL
51+ (Just (Template [ TrimR
51− , TrimR
52 , Chunk "\n"
52 , Chunk "\n"
53 , Expr (Ident (TemplateKey "body"))
53 , Expr (Ident (TemplateKey "body"))
54 , Chunk "\n"
54 , Chunk "\n"
55+ , TrimL
55 ]))
56 ]))
56− , TrimL
57 , TrimR
57 , TrimR
58 ]
58 ]
59 @=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
59 @=? readTemplate "$-if(body)-$\n$body$\n$-else-$\n$body$\n$-endif-$"
60 -- 'For' trim check.
60 -- 'For' trim check.
61 , Template
61 , Template
62 [ TrimL
62 [ TrimL
63− , TrimR
64 , For (Ident (TemplateKey "authors"))
63 , For (Ident (TemplateKey "authors"))
64+ (Template [ TrimR
65− (Template [Chunk "\n body \n"])
65+ , Chunk "\n body \n"
66+ , TrimL])
66 Nothing
67 Nothing
67− , TrimL
68 , TrimR
68 , TrimR
69 ]
69 ]
70 @=? readTemplate "$-for(authors)-$\n body \n$-endfor-$"
70 @=? readTemplate "$-for(authors)-$\n body \n$-endfor-$"