module Darcs.Patch.Match
(
matchParser
, helpOnMatchers
, addInternalMatcher
, matchFirstPatchset
, matchSecondPatchset
, splitSecondFL
, matchPatch
, matchAPatch
, getNonrangeMatchS
, firstMatch
, secondMatch
, haveNonrangeMatch
, haveNonrangeExplicitMatch
, havePatchsetMatch
, checkMatchSyntax
, applyInvToMatcher
, nonrangeMatcher
, InclusiveOrExclusive(..)
, matchExists
, applyNInv
, hasIndexRange
, getMatchingTag
, matchAPatchset
, getFirstMatchS
, nonrangeMatcherIsTag
, MatchFlag(..)
) where
import Prelude ()
import Darcs.Prelude
import Text.ParserCombinators.Parsec
( parse
, CharParser
, (<?>)
, (<|>)
, noneOf
, option
, eof
, many
, try
, between
, spaces
, char
, oneOf
, string
, choice
)
import Text.ParserCombinators.Parsec.Expr
( OperatorTable
, Assoc( AssocLeft )
, Operator ( Infix, Prefix )
, buildExpressionParser
)
import Text.Regex ( mkRegex, matchRegex )
import Data.Maybe ( isJust )
import System.IO.Unsafe ( unsafePerformIO )
import Control.Monad ( when )
import Data.List ( isPrefixOf, intercalate )
import Data.Char ( toLower )
import Darcs.Util.Path ( AbsolutePath )
import Darcs.Patch
( IsRepoType
, hunkMatches
, listTouchedFiles
, invert
, invertRL
, apply
)
import Darcs.Patch.Info ( justName, justAuthor, justLog, makePatchname,
piDate )
import Darcs.Patch.Named.Wrapped
( WrappedNamed
, patch2patchinfo
)
import qualified Data.ByteString.Char8 as BC
import Darcs.Patch.Dummy ( DummyPatch )
import Darcs.Patch.Matchable ( Matchable )
import Darcs.Patch.MonadProgress ( MonadProgress )
import Darcs.Patch.Named.Wrapped ( runInternalChecker, namedIsInternal, namedInternalChecker )
import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, conscientiously, hopefully )
import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, patchSet2RL, Origin )
import Darcs.Patch.Type ( PatchType(..) )
import Darcs.Patch.Apply( Apply, ApplyState )
import Darcs.Patch.ApplyPatches( applyPatches )
import Darcs.Patch.Depends ( getPatchesBeyondTag, splitOnTag )
import Darcs.Patch.Invert( Invert )
import Darcs.Patch.Witnesses.Eq ( isIsEq )
import Darcs.Patch.Witnesses.Ordered ( RL(..), snocRLSealed, FL(..), (:>)(..) )
import Darcs.Patch.Witnesses.Sealed
( FlippedSeal(..), Sealed2(..),
seal, flipSeal, seal2, unsealFlipped, unseal2, unseal )
import Darcs.Util.Printer ( text, ($$) )
import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )
import Darcs.Util.DateMatcher ( parseDateMatcher )
import Darcs.Util.Tree ( Tree )
type MatchFun rt p = Sealed2 (PatchInfoAnd rt p) -> Bool
data Matcher rt p = MATCH String (MatchFun rt p)
instance Show (Matcher rt p) where
show (MATCH s _) = '"':s ++ "\""
data MatchFlag =
OnePattern String
| SeveralPattern String
| AfterPattern String
| UpToPattern String
| OnePatch String
| OneHash String
| AfterHash String
| UpToHash String
| SeveralPatch String
| AfterPatch String
| UpToPatch String
| OneTag String
| AfterTag String
| UpToTag String
| LastN Int
| PatchIndexRange Int Int
| Context AbsolutePath
deriving ( Show )
makeMatcher :: String -> MatchFun rt p -> Matcher rt p
makeMatcher = MATCH
applyMatcher :: Matcher rt p -> PatchInfoAnd rt p wX wY -> Bool
applyMatcher (MATCH _ m) = m . seal2
parseMatch :: Matchable p => String -> Either String (MatchFun rt p)
parseMatch pattern =
case parse matchParser "match" pattern of
Left err -> Left $ "Invalid --match pattern '"++ pattern ++
"'.\n"++ unlines (map (" "++) $ lines $ show err)
Right m -> Right m
matchPattern :: Matchable p => String -> Matcher rt p
matchPattern pattern =
case parseMatch pattern of
Left err -> error err
Right m -> makeMatcher pattern m
addInternalMatcher :: (IsRepoType rt) => Maybe (Matcher rt p) -> Maybe (Matcher rt p)
addInternalMatcher om =
case namedInternalChecker of
Nothing -> om
Just f ->
let matchFun = unseal2 (not . isIsEq . runInternalChecker f . hopefully)
in case om of
Nothing -> Just (MATCH "internal patch" matchFun)
Just (MATCH s oldFun) -> Just (MATCH s (\p -> matchFun p && oldFun p))
matchParser :: Matchable p => CharParser st (MatchFun rt p)
matchParser = submatcher <?> helpfulErrorMsg
where
submatcher = do
m <- option matchAnyPatch submatch
eof
return m
helpfulErrorMsg = "valid expressions over: "
++ intercalate ", " (map (\(name, _, _, _, _) -> name) ps)
++ "\nfor more help, see `darcs help patterns`."
ps :: [(String, String, String, [String], String -> MatchFun rt DummyPatch)]
ps = primitiveMatchers
matchAnyPatch :: MatchFun rt p
matchAnyPatch = const True
submatch :: Matchable p => CharParser st (MatchFun rt p)
submatch = buildExpressionParser table match
table :: OperatorTable Char st (MatchFun rt p)
table = [ [prefix "not" negate_match,
prefix "!" negate_match ]
, [binary "||" or_match,
binary "or" or_match,
binary "&&" and_match,
binary "and" and_match ]
]
where binary name fun = Infix (tryNameAndUseFun name fun) AssocLeft
prefix name fun = Prefix $ tryNameAndUseFun name fun
tryNameAndUseFun name fun = do _ <- trystring name
spaces
return fun
negate_match a p = not (a p)
or_match m1 m2 p = m1 p || m2 p
and_match m1 m2 p = m1 p && m2 p
trystring :: String -> CharParser st String
trystring s = try $ string s
match :: Matchable p => CharParser st (MatchFun rt p)
match = between spaces spaces (parens submatch <|> choice matchers_)
where
matchers_ = map createMatchHelper primitiveMatchers
createMatchHelper :: (String, String, String, [String], String -> MatchFun rt p)
-> CharParser st (MatchFun rt p)
createMatchHelper (key,_,_,_,matcher) =
do _ <- trystring key
spaces
q <- quoted
return $ matcher q
helpOnMatchers :: [String]
helpOnMatchers =
["Selecting Patches:",
"",
"The --patches option yields patches with names matching an *extended*",
"regular expression. See regex(7) for details. The --matches option",
"yields patches that match a logical (Boolean) expression: one or more",
"primitive expressions combined by grouping (parentheses) and the",
"complement (not), conjunction (and) and disjunction (or) operators.",
"The C notation for logic operators (!, && and ||) can also be used.",
"",
"- --patches=regex is a synonym for --matches='name regex'",
"- --hash=HASH is a synonym for --matches='hash HASH'",
"- --from-patch and --to-patch are synonyms for --from-match='name... and --to-match='name...",
"- --from-patch and --to-match can be unproblematically combined:",
" `darcs log --from-patch='html.*documentation' --to-match='date 20040212'`",
"",
"The following primitive Boolean expressions are supported:"
,""]
++ keywords
++ ["", "Here are some examples:", ""]
++ examples
where
ps :: [(String, String, String, [String], String -> MatchFun rt DummyPatch)]
ps = primitiveMatchers
keywords = [showKeyword (unwords [k,a]) d | (k,a,d,_,_) <- ps]
examples = [showExample k e | (k,_,_,es,_) <- ps, e <- es]
showKeyword keyword description =
" " ++ keyword ++ " - " ++ description ++ "."
showExample keyword example =
" darcs log --match "
++ "'" ++ keyword ++ " " ++ example ++ "'"
primitiveMatchers :: Matchable p => [(String, String, String, [String], String -> MatchFun rt p)]
primitiveMatchers =
[ ("exact", "STRING", "check literal STRING is equal to patch name"
, ["\"Resolve issue17: use dynamic memory allocation.\""]
, exactmatch )
, ("name", "REGEX", "match REGEX against patch name"
, ["issue17", "\"^[Rr]esolve issue17\\>\""]
, namematch )
, ("author", "REGEX", "match REGEX against patch author"
, ["\"David Roundy\"", "droundy", "droundy@darcs.net"]
, authormatch )
, ("hunk", "REGEX", "match REGEX against contents of a hunk patch"
, ["\"foo = 2\"", "\"^instance .* Foo where$\""]
, hunkmatch )
, ("comment", "REGEX", "match REGEX against the full log message"
, ["\"prevent deadlocks\""]
, logmatch )
, ("hash", "HASH", "match HASH against (a prefix of) the hash of a patch"
, ["c719567e92c3b0ab9eddd5290b705712b8b918ef","c7195"]
, hashmatch )
, ("date", "DATE", "match DATE against the patch date"
, ["\"2006-04-02 22:41\"", "\"tea time yesterday\""]
, datematch )
, ("touch", "REGEX", "match file paths for a patch"
, ["src/foo.c", "src/", "\"src/*.(c|h)\""]
, touchmatch ) ]
parens :: CharParser st (MatchFun rt p)
-> CharParser st (MatchFun rt p)
parens = between (string "(") (string ")")
quoted :: CharParser st String
quoted = between (char '"') (char '"')
(many $ do { _ <- char '\\'
; try (oneOf "\\\"") <|> return '\\'
}
<|> noneOf "\"")
<|> between spaces spaces (many $ noneOf " ()")
<?> "string"
datematch, hashmatch, authormatch, exactmatch, namematch, logmatch
:: String -> MatchFun rt p
hunkmatch, touchmatch
:: Matchable p => String -> MatchFun rt p
namematch r (Sealed2 hp) = isJust $ matchRegex (mkRegex r) $ justName (info hp)
exactmatch r (Sealed2 hp) = r == justName (info hp)
authormatch a (Sealed2 hp) = isJust $ matchRegex (mkRegex a) $ justAuthor (info hp)
logmatch l (Sealed2 hp) = isJust $ matchRegex (mkRegex l) $ justLog (info hp)
hunkmatch r (Sealed2 hp) = let regexMatcher = isJust . matchRegex (mkRegex r) . BC.unpack
in hunkMatches regexMatcher hp
hashmatch h (Sealed2 hp) = let rh = show $ makePatchname (info hp)
lh = map toLower h
in (lh `isPrefixOf` rh) || (lh == rh ++ ".gz")
datematch d (Sealed2 hp) = let dm = unsafePerformIO $ parseDateMatcher d
in dm $ piDate (info hp)
touchmatch r (Sealed2 hp) = let files = listTouchedFiles hp
in any (isJust . matchRegex (mkRegex r)) files
data InclusiveOrExclusive = Inclusive | Exclusive deriving Eq
data IncludeInternalPatches = IncludeInternalPatches | ExcludeInternalPatches
deriving Eq
haveNonrangeMatch :: forall rt p . (IsRepoType rt, Matchable p)
=> PatchType rt p -> [MatchFlag] -> Bool
haveNonrangeMatch pt fs = haveNonrangeMatch' IncludeInternalPatches pt fs
haveNonrangeExplicitMatch :: forall rt p . (IsRepoType rt, Matchable p)
=> PatchType rt p -> [MatchFlag] -> Bool
haveNonrangeExplicitMatch pt fs = haveNonrangeMatch' ExcludeInternalPatches pt fs
haveNonrangeMatch' :: forall rt p . (IsRepoType rt, Matchable p)
=> IncludeInternalPatches -> PatchType rt p -> [MatchFlag] -> Bool
haveNonrangeMatch' i _ fs =
case hasIndexRange fs of Just (m,n) | m == n -> True; _ -> False
|| isJust (nonrangeMatch::Maybe (Matcher rt p))
where
nonrangeMatch | i == IncludeInternalPatches = nonrangeMatcher fs
| otherwise = nonrangeMatcherArgs fs
havePatchsetMatch
:: forall rt p
. (IsRepoType rt, Matchable p)
=> PatchType rt p -> [MatchFlag] -> Bool
havePatchsetMatch _ fs = isJust (nonrangeMatcher fs::Maybe (Matcher rt p)) || hasC fs
where hasC [] = False
hasC (Context _:_) = True
hasC (_:xs) = hasC xs
getNonrangeMatchS :: ( ApplyMonad (ApplyState p) m, MonadProgress m
, IsRepoType rt, Matchable p, ApplyState p ~ Tree
)
=> [MatchFlag]
-> PatchSet rt p Origin wX
-> m ()
getNonrangeMatchS fs repo =
case nonrangeMatcher fs of
Just m -> if nonrangeMatcherIsTag fs
then getTagS m repo
else getMatcherS Exclusive m repo
Nothing -> fail "Pattern not specified in getNonrangeMatch."
firstMatch :: [MatchFlag] -> Bool
firstMatch fs = isJust (hasLastn fs)
|| isJust (firstMatcher fs::Maybe (Matcher rt DummyPatch))
|| isJust (hasIndexRange fs)
getFirstMatchS :: (ApplyMonad (ApplyState p) m, MonadProgress m, Matchable p, IsRepoType rt)
=> [MatchFlag] -> PatchSet rt p Origin wX -> m ()
getFirstMatchS fs repo =
case hasLastn fs of
Just n -> unpullLastN repo n
Nothing ->
case hasIndexRange fs of
Just (_,b) -> unpullLastN repo b
Nothing ->
case firstMatcher fs of
Nothing -> fail "Pattern not specified in getFirstMatchS."
Just m -> if firstMatcherIsTag fs
then getTagS m repo
else getMatcherS Inclusive m repo
secondMatch :: [MatchFlag] -> Bool
secondMatch fs = isJust (secondMatcher fs::Maybe (Matcher rt DummyPatch)) || isJust (hasIndexRange fs)
unpullLastN :: (Apply p, Invert p, ApplyMonad (ApplyState p) m, MonadProgress m, IsRepoType rt)
=> PatchSet rt p wX wY
-> Int
-> m ()
unpullLastN repo n = applyInvRL `unsealFlipped` safetake n (patchSet2RL repo)
checkMatchSyntax :: [MatchFlag] -> IO ()
checkMatchSyntax opts =
case getMatchPattern opts of
Nothing -> return ()
Just p -> either fail (const $ return ()) (parseMatch p::Either String (MatchFun rt DummyPatch))
getMatchPattern :: [MatchFlag] -> Maybe String
getMatchPattern [] = Nothing
getMatchPattern (OnePattern m:_) = Just m
getMatchPattern (SeveralPattern m:_) = Just m
getMatchPattern (_:fs) = getMatchPattern fs
tagmatch :: String -> Matcher rt p
tagmatch r = makeMatcher ("tag-name "++r) tm
where tm (Sealed2 p) =
let n = justName (info p) in
"TAG " `isPrefixOf` n && isJust (matchRegex (mkRegex r) $ drop 4 n)
patchmatch :: String -> Matcher rt p
patchmatch r = makeMatcher ("patch-name "++r) (namematch r)
hashmatch' :: String -> Matcher rt p
hashmatch' r = makeMatcher ("hash "++r) (hashmatch r)
strictJust :: a -> Maybe a
strictJust x = Just $! x
nonrangeMatcher :: (IsRepoType rt, Matchable p) => [MatchFlag] -> Maybe (Matcher rt p)
nonrangeMatcherArgs :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p)
nonrangeMatcher fs = addInternalMatcher $ nonrangeMatcherArgs fs
nonrangeMatcherArgs [] = Nothing
nonrangeMatcherArgs (OnePattern m:_) = strictJust $ matchPattern m
nonrangeMatcherArgs (OneTag t:_) = strictJust $ tagmatch t
nonrangeMatcherArgs (OnePatch p:_) = strictJust $ patchmatch p
nonrangeMatcherArgs (OneHash h:_) = strictJust $ hashmatch' h
nonrangeMatcherArgs (SeveralPattern m:_) = strictJust $ matchPattern m
nonrangeMatcherArgs (SeveralPatch p:_) = strictJust $ patchmatch p
nonrangeMatcherArgs (_:fs) = nonrangeMatcherArgs fs
nonrangeMatcherIsTag :: [MatchFlag] -> Bool
nonrangeMatcherIsTag [] = False
nonrangeMatcherIsTag (OneTag _:_) = True
nonrangeMatcherIsTag (_:fs) = nonrangeMatcherIsTag fs
firstMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p)
firstMatcher [] = Nothing
firstMatcher (OnePattern m:_) = strictJust $ matchPattern m
firstMatcher (AfterPattern m:_) = strictJust $ matchPattern m
firstMatcher (AfterTag t:_) = strictJust $ tagmatch t
firstMatcher (OnePatch p:_) = strictJust $ patchmatch p
firstMatcher (AfterPatch p:_) = strictJust $ patchmatch p
firstMatcher (OneHash h:_) = strictJust $ hashmatch' h
firstMatcher (AfterHash h:_) = strictJust $ hashmatch' h
firstMatcher (_:fs) = firstMatcher fs
firstMatcherIsTag :: [MatchFlag] -> Bool
firstMatcherIsTag [] = False
firstMatcherIsTag (AfterTag _:_) = True
firstMatcherIsTag (_:fs) = firstMatcherIsTag fs
secondMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p)
secondMatcher [] = Nothing
secondMatcher (OnePattern m:_) = strictJust $ matchPattern m
secondMatcher (UpToPattern m:_) = strictJust $ matchPattern m
secondMatcher (OnePatch p:_) = strictJust $ patchmatch p
secondMatcher (UpToPatch p:_) = strictJust $ patchmatch p
secondMatcher (OneHash h:_) = strictJust $ hashmatch' h
secondMatcher (UpToHash h:_) = strictJust $ hashmatch' h
secondMatcher (UpToTag t:_) = strictJust $ tagmatch t
secondMatcher (_:fs) = secondMatcher fs
secondMatcherIsTag :: [MatchFlag] -> Bool
secondMatcherIsTag [] = False
secondMatcherIsTag (UpToTag _:_) = True
secondMatcherIsTag (_:fs) = secondMatcherIsTag fs
matchAPatch :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchInfoAnd rt p wX wY -> Bool
matchAPatch fs p =
case nonrangeMatcher fs of
Nothing -> True
Just m -> applyMatcher m p
matchPatch :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX -> Sealed2 (WrappedNamed rt p)
matchPatch fs ps =
case hasIndexRange fs of
Just (a,a') | a == a' -> case unseal myhead $ dropn (a1) ps of
Just (Sealed2 p) -> seal2 $ hopefully p
Nothing -> error "Patch out of range!"
| otherwise -> bug ("Invalid index range match given to matchPatch: "++
show (PatchIndexRange a a'))
where myhead :: PatchSet rt p wStart wX -> Maybe (Sealed2 (PatchInfoAnd rt p))
myhead (PatchSet (_ :<: Tagged t _ _) NilRL) = Just $ seal2 t
myhead (PatchSet _ (_:<:x)) = Just $ seal2 x
myhead _ = Nothing
Nothing -> case nonrangeMatcher fs of
Nothing -> bug "Couldn't matchPatch"
Just m -> findAPatch m ps
hasLastn :: [MatchFlag] -> Maybe Int
hasLastn [] = Nothing
hasLastn (LastN (1):_) = error "--last requires a positive integer argument."
hasLastn (LastN n:_) = Just n
hasLastn (_:fs) = hasLastn fs
hasIndexRange :: [MatchFlag] -> Maybe (Int,Int)
hasIndexRange [] = Nothing
hasIndexRange (PatchIndexRange x y:_) = Just (x,y)
hasIndexRange (_:fs) = hasIndexRange fs
matchFirstPatchset :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX
-> SealedPatchSet rt p wStart
matchFirstPatchset fs patchset =
case hasLastn fs of
Just n -> dropn n patchset
Nothing ->
case hasIndexRange fs of
Just (_,b) -> dropn b patchset
Nothing ->
case firstMatcher fs of
Nothing -> bug "Couldn't matchFirstPatchset"
Just m -> unseal (dropn 1) $ if firstMatcherIsTag fs
then getMatchingTag m patchset
else matchAPatchset m patchset
dropn :: IsRepoType rt => Int -> PatchSet rt p wStart wX -> SealedPatchSet rt p wStart
dropn n ps | n <= 0 = seal ps
dropn n (PatchSet (ts :<: Tagged t _ ps) NilRL) = dropn n $ PatchSet ts (ps:<:t)
dropn _ (PatchSet NilRL NilRL) = seal $ PatchSet NilRL NilRL
dropn n (PatchSet ts (ps:<:p))
| isIsEq (namedIsInternal (hopefully p))
= dropn n $ PatchSet ts ps
dropn n (PatchSet ts (ps:<:_)) = dropn (n1) $ PatchSet ts ps
matchSecondPatchset :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX
-> SealedPatchSet rt p wStart
matchSecondPatchset fs ps =
case hasIndexRange fs of
Just (a,_) -> dropn (a1) ps
Nothing ->
case secondMatcher fs of
Nothing -> bug "Couldn't matchSecondPatchset"
Just m -> if secondMatcherIsTag fs
then getMatchingTag m ps
else matchAPatchset m ps
splitSecondFL :: Matchable p
=> (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd rt p))
-> [MatchFlag]
-> FL q wX wY
-> (FL q :> FL q) wX wY
splitSecondFL extract fs ps =
case hasIndexRange fs of
Just _ ->
bug "index matches not supported by splitSecondPatchesFL"
Nothing ->
case secondMatcher fs of
Nothing -> bug "Couldn't splitSecondPatches"
Just m -> splitMatchFL extract m ps
findAPatch :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX -> Sealed2 (WrappedNamed rt p)
findAPatch m (PatchSet NilRL NilRL) = error $ "Couldn't find patch matching " ++ show m
findAPatch m (PatchSet (ts :<: Tagged t _ ps) NilRL) = findAPatch m (PatchSet ts (ps:<:t))
findAPatch m (PatchSet ts (ps:<:p)) | applyMatcher m p = seal2 $ hopefully p
| otherwise = findAPatch m (PatchSet ts ps)
matchAPatchset :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX
-> SealedPatchSet rt p wStart
matchAPatchset m (PatchSet NilRL NilRL) = error $ "Couldn't find patch matching " ++ show m
matchAPatchset m (PatchSet (ts :<: Tagged t _ ps) NilRL) = matchAPatchset m (PatchSet ts (ps:<:t))
matchAPatchset m (PatchSet ts (ps:<:p)) | applyMatcher m p = seal (PatchSet ts (ps:<:p))
| otherwise = matchAPatchset m (PatchSet ts ps)
getMatchingTag :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX -> SealedPatchSet rt p wStart
getMatchingTag m (PatchSet NilRL NilRL) = error $ "Couldn't find a tag matching " ++ show m
getMatchingTag m (PatchSet (ts :<: Tagged t _ ps) NilRL) = getMatchingTag m (PatchSet ts (ps:<:t))
getMatchingTag m (PatchSet ts (ps:<:p))
| applyMatcher m p =
case splitOnTag (info p) (PatchSet ts (ps:<:p)) of
Nothing -> bug "splitOnTag couldn't find tag we explicitly provided!"
Just (patchSet :> _) -> seal patchSet
| otherwise = getMatchingTag m (PatchSet ts ps)
splitMatchFL :: Matchable p => (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd rt p)) -> Matcher rt p -> FL q wX wY -> (FL q :> FL q) wX wY
splitMatchFL _extract m NilFL = error $ "Couldn't find patch matching " ++ show m
splitMatchFL extract m (p :>: ps)
| unseal2 (applyMatcher m) . extract $ p = (p :>: NilFL) :> ps
| otherwise = case splitMatchFL extract m ps of
before :> after -> (p :>: before) :> after
matchExists :: Matcher rt p -> PatchSet rt p wStart wX -> Bool
matchExists _ (PatchSet NilRL NilRL) = False
matchExists m (PatchSet (ts :<: Tagged t _ ps) NilRL) = matchExists m (PatchSet ts (ps:<:t))
matchExists m (PatchSet ts (ps:<:p)) | applyMatcher m p = True
| otherwise = matchExists m (PatchSet ts ps)
applyInvToMatcher :: (Matchable p, ApplyMonad (ApplyState p) m)
=> InclusiveOrExclusive -> Matcher rt p -> PatchSet rt p Origin wX -> m ()
applyInvToMatcher _ _ (PatchSet NilRL NilRL) = impossible
applyInvToMatcher ioe m (PatchSet (ts :<: Tagged t _ ps) NilRL) = applyInvToMatcher ioe m
(PatchSet ts (ps:<:t))
applyInvToMatcher ioe m (PatchSet xs (ps:<:p))
| applyMatcher m p = when (ioe == Inclusive) (applyInvp p)
| otherwise = applyInvp p >> applyInvToMatcher ioe m (PatchSet xs ps)
applyNInv :: (Matchable p, ApplyMonad (ApplyState p) m) => Int -> PatchSet rt p Origin wX -> m ()
applyNInv n _ | n <= 0 = return ()
applyNInv _ (PatchSet NilRL NilRL) = error "Index out of range."
applyNInv n (PatchSet (ts :<: Tagged t _ ps) NilRL) =
applyNInv n (PatchSet ts (ps :<: t))
applyNInv n (PatchSet xs (ps :<: p)) =
applyInvp p >> applyNInv (n 1) (PatchSet xs ps)
getMatcherS :: (ApplyMonad (ApplyState p) m, Matchable p) =>
InclusiveOrExclusive -> Matcher rt p -> PatchSet rt p Origin wX -> m ()
getMatcherS ioe m repo =
if matchExists m repo
then applyInvToMatcher ioe m repo
else fail $ "Couldn't match pattern "++ show m
getTagS :: (ApplyMonad (ApplyState p) m, MonadProgress m, Matchable p) =>
Matcher rt p -> PatchSet rt p Origin wX -> m ()
getTagS matcher repo = do
let pinfo = patch2patchinfo `unseal2` findAPatch matcher repo
case getPatchesBeyondTag pinfo repo of
FlippedSeal extras -> applyInvRL extras
applyInvp :: (Apply p, Invert p, ApplyMonad (ApplyState p) m) => PatchInfoAnd rt p wX wY -> m ()
applyInvp hp = apply (invert $ fromHopefully hp)
where fromHopefully = conscientiously $ \e ->
text "Sorry, patch not available:"
$$ e
$$ text ""
$$ text "If you think what you're trying to do is ok then"
$$ text "report this as a bug on the darcs-user list."
safetake :: IsRepoType rt => Int -> RL (PatchInfoAnd rt p) wX wY -> FlippedSeal (RL (PatchInfoAnd rt p)) wY
safetake 0 _ = flipSeal NilRL
safetake _ NilRL = error "There aren't that many patches..."
safetake i (as:<:a) | isIsEq (namedIsInternal (hopefully a)) = safetake i as `snocRLSealed` a
safetake i (as:<:a) = safetake (i1) as `snocRLSealed` a
applyInvRL :: (Apply p, Invert p, ApplyMonad (ApplyState p) m, MonadProgress m) => RL (PatchInfoAnd rt p) wX wR -> m ()
applyInvRL = applyPatches . invertRL