1-- | Module providing pattern matching and capturing on 'Identifier's.
3-- A very simple pattern could be, for example, @foo\/bar@. This pattern will
4-- only match the exact @foo\/bar@ identifier.
6-- To match more than one identifier, there are different captures that one can
9-- * @*@: matches at most one element of an identifier;
11-- * @**@: matches one or more elements of an identifier.
15-- * @foo\/*@ will match @foo\/bar@ and @foo\/foo@, but not @foo\/bar\/qux@;
17-- * @**@ will match any identifier;
19-- * @foo\/**@ will match @foo\/bar@ and @foo\/bar\/qux@, but not @bar\/foo@;
21-- * @foo\/*.html@ will match all HTML files in the @foo\/@ directory.
23-- The 'match' function allows the user to get access to the elements captured
24-- by the capture elements in the pattern.
26module Hakyll.Core.Identifier.Pattern
37import Data.List (isPrefixOf, inits, tails)
38import Control.Arrow ((&&&), (>>>))
39import Control.Monad (msum)
40import Data.Maybe (isJust)
41import Data.Monoid (mempty, mappend)
43import GHC.Exts (IsString, fromString)
45import Hakyll.Core.Identifier
47-- | One base element of a pattern
49data PatternComponent = Capture
54-- | Type that allows matching on identifiers
56newtype Pattern = Pattern {unPattern :: [PatternComponent]}
59instance IsString Pattern where
60 fromString = parsePattern
62-- | Parse a pattern from a string
64parsePattern :: String -> Pattern
65parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIdentifier . parseIdentifier
68 let (chunk, rest) = break (`elem` "\\*") str
70 ('\\' : x : xs) -> Literal (chunk ++ [x]) : parse' xs
71 ('*' : '*' : xs) -> Literal chunk : CaptureMany : parse' xs
72 ('*' : xs) -> Literal chunk : Capture : parse' xs
73 xs -> Literal chunk : Literal xs : []
75-- | Match an identifier against a pattern, generating a list of captures
77match :: Pattern -> Identifier -> Maybe [Identifier]
78match p (Identifier i) = fmap (map Identifier) $ match' (unPattern p) i
80-- | Check if an identifier matches a pattern
82doesMatch :: Pattern -> Identifier -> Bool
83doesMatch p = isJust . match p
85-- | Given a list of identifiers, retain only those who match the given pattern
87matches :: Pattern -> [Identifier] -> [Identifier]
88matches p = filter (doesMatch p)
90-- | Split a list at every possible point, generate a list of (init, tail)
91-- cases. The result is sorted with inits decreasing in length.
93splits :: [a] -> [([a], [a])]
94splits = inits &&& tails >>> uncurry zip >>> reverse
96-- | Internal verion of 'match'
98match' :: [PatternComponent] -> String -> Maybe [String]
99match' [] [] = Just [] -- An empty match
100match' [] _ = Nothing -- No match
101-- match' _ [] = Nothing -- No match
102match' (Literal l : ms) str
103 -- Match the literal against the string
104 | l `isPrefixOf` str = match' ms $ drop (length l) str
105 | otherwise = Nothing
106match' (Capture : ms) str =
107 -- Match until the next /
108 let (chunk, rest) = break (== '/') str
109 in msum $ [ fmap (i :) (match' ms (t ++ rest)) | (i, t) <- splits chunk ]
110match' (CaptureMany : ms) str =
112 msum $ [ fmap (i :) (match' ms t) | (i, t) <- splits str ]
114-- | Create an identifier from a pattern by filling in the captures with a given
119-- > fromCapture (parsePattern "tags/*") (parseIdentifier "foo")
125fromCapture :: Pattern -> Identifier -> Identifier
126fromCapture pattern = fromCaptures pattern . repeat
128-- | Simplified version of 'fromCapture' which takes a 'String' instead of an
131-- > fromCaptureString (parsePattern "tags/*") "foo"
137fromCaptureString :: Pattern -> String -> Identifier
138fromCaptureString pattern = fromCapture pattern . parseIdentifier
140-- | Create an identifier from a pattern by filling in the captures with the
141-- given list of strings
143fromCaptures :: Pattern -> [Identifier] -> Identifier
144fromCaptures (Pattern []) _ = mempty
145fromCaptures (Pattern (m : ms)) [] = case m of
146 Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) []
147 _ -> error $ "Hakyll.Core.Identifier.Pattern.fromCaptures: "
148 ++ "identifier list exhausted"
149fromCaptures (Pattern (m : ms)) ids@(i : is) = case m of
150 Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) ids
151 _ -> i `mappend` fromCaptures (Pattern ms) is
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list