@@ -37,13 +37,13 @@ module Hakyll.Core.Identifier.Pattern
3737 , fromList
3838 , fromRegex
3939 , fromVersion
40+ , hasVersion
41+ , hasNoVersion
4042
41− -- * Manipulating patterns
43+ -- * Composing patterns
4244 , (.&&.)
4345 , (.||.)
4446 , complement
45− , withVersion
46− , noVersion
4747
4848 -- * Applying patterns
4949 , matches
@@ -105,7 +105,6 @@ data Pattern
105105 = Everything
106106 | Complement Pattern
107107 | And Pattern Pattern
108− | Or Pattern Pattern
109108 | Glob [GlobComponent]
110109 | List (Set Identifier)
111110 | Regex String
@@ -118,20 +117,18 @@ instance Binary Pattern where
118117 put Everything = putWord8 0
119118 put (Complement p) = putWord8 1 >> put p
120119 put (And x y) = putWord8 2 >> put x >> put y
121− put (Or x y) = putWord8 3 >> put x >> put y
122− put (Glob g) = putWord8 4 >> put g
123− put (List is) = putWord8 5 >> put is
124− put (Regex r) = putWord8 6 >> put r
125− put (Version v) = putWord8 7 >> put v
120+ put (Glob g) = putWord8 3 >> put g
121+ put (List is) = putWord8 4 >> put is
122+ put (Regex r) = putWord8 5 >> put r
123+ put (Version v) = putWord8 6 >> put v
126124
127125 get = getWord8 >>= \t -> case t of
128126 0 -> pure Everything
129127 1 -> Complement <$> get
130128 2 -> And <$> get <*> get
131− 3 -> Or <$> get <*> get
132− 4 -> Glob <$> get
133− 5 -> List <$> get
134− 6 -> Regex <$> get
129+ 3 -> Glob <$> get
130+ 4 -> List <$> get
131+ 5 -> Regex <$> get
135132 _ -> Version <$> get
136133
137134
@@ -146,19 +143,6 @@ instance Monoid Pattern where
146143 mappend = (.&&.)
147144
148145
149−--------------------------------------------------------------------------------
150−-- | This is necessary for good 'isLiteral' results
151−optimize :: Pattern -> Pattern
152−optimize (Complement x) = Complement (optimize x)
153−optimize (And x Everything) = x
154−optimize (And Everything y) = y
155−optimize (And x y) = And (optimize x) (optimize y)
156−optimize (Or _ Everything) = Everything
157−optimize (Or Everything _) = Everything
158−optimize (Or x y) = Or (optimize x) (optimize y)
159−optimize p = p
160−
161−
162146--------------------------------------------------------------------------------
163147-- | Parse a pattern from a string
164148fromGlob :: String -> Pattern
@@ -174,7 +158,24 @@ fromGlob = Glob . parse'
174158
175159
176160--------------------------------------------------------------------------------
177−-- | Create a 'Pattern' from a list of 'Identifier's it should match
161+-- | Create a 'Pattern' from a list of 'Identifier's it should match.
162+--
163+-- /Warning/: use this carefully with 'hasNoVersion' and 'hasVersion'. The
164+-- 'Identifier's in the list /already/ have versions assigned, and the pattern
165+-- will then only match the intersection of both versions.
166+--
167+-- A more concrete example,
168+--
169+-- > fromList ["foo.markdown"] .&&. hasVersion "pdf"
170+--
171+-- will not match anything! The @"foo.markdown"@ 'Identifier' has no version
172+-- assigned, so the LHS of '.&&.' will only match this 'Identifier' with no
173+-- version. The RHS only matches 'Identifier's with version set to @"pdf"@ --
174+-- hence, this pattern matches nothing.
175+--
176+-- The correct way to use this is:
177+--
178+-- > fromList (map (setVersion "pdf") ["foo.markdown"])
178179fromList :: [Identifier] -> Pattern
179180fromList = List . S.fromList
180181
@@ -195,17 +196,33 @@ fromVersion :: Maybe String -> Pattern
195196fromVersion = Version
196197
197198
199+--------------------------------------------------------------------------------
200+-- | Specify a version, e.g.
201+--
202+-- > "foo/*.markdown" .&&. hasVersion "pdf"
203+hasVersion :: String -> Pattern
204+hasVersion = fromVersion . Just
205+
206+
207+--------------------------------------------------------------------------------
208+-- | Match only if the identifier has no version set, e.g.
209+--
210+-- > "foo/*.markdown" .&&. hasNoVersion
211+hasNoVersion :: Pattern
212+hasNoVersion = fromVersion Nothing
213+
214+
198215--------------------------------------------------------------------------------
199216-- | '&&' for patterns: the given identifier must match both subterms
200217(.&&.) :: Pattern -> Pattern -> Pattern
201−x .&&. y = optimize (And x y)
218+x .&&. y = And x y
202219infixr 3 .&&.
203220
204221
205222--------------------------------------------------------------------------------
206223-- | '||' for patterns: the given identifier must match any subterm
207224(.||.) :: Pattern -> Pattern -> Pattern
208−x .||. y = optimize (Or x y)
225+x .||. y = complement (complement x `And` complement y) -- De Morgan's law
209226infixr 2 .||.
210227
211228
@@ -219,27 +236,12 @@ complement :: Pattern -> Pattern
219236complement = Complement
220237
221238
222−--------------------------------------------------------------------------------
223−-- | Specify a version, e.g.
224−--
225−-- > "foo/*.markdown" `withVersion` "pdf"
226−withVersion :: Pattern -> String -> Pattern
227−withVersion p v = optimize $ And p $ fromVersion $ Just v
228−
229−
230−--------------------------------------------------------------------------------
231−-- | Match only if the identifier has no version set
232−noVersion :: Pattern -> Pattern
233−noVersion p = optimize $ And p $ fromVersion Nothing
234−
235−
236239--------------------------------------------------------------------------------
237240-- | Check if an identifier matches a pattern
238241matches :: Pattern -> Identifier -> Bool
239242matches Everything _ = True
240243matches (Complement p) i = not $ matches p i
241244matches (And x y) i = matches x i && matches y i
242−matches (Or x y) i = matches x i || matches y i
243245matches (Glob p) i = isJust $ capture (Glob p) i
244246matches (List l) i = i `S.member` l
245247matches (Regex r) i = toFilePath i =~ r