% Copyright (C) 2002-2004 David Roundy % % This program is free software; you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation; either version 2, or (at your option) % any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program; if not, write to the Free Software Foundation, % Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \section{darcs record} \begin{code} module PatchChoices ( PatchChoices, patch_choices, is_patch_first, get_first_choice, get_middle_choice, get_last_choice, force_first, force_firsts, force_last, force_lasts, force_matching_first, force_matching_last, make_uncertain, make_everything_later, ) where import Monad ( liftM ) import Patch \end{code} PatchChoices divides a sequence of patches into three sets: ``first'', ``middle'' and ``last'', such that all patches can be applied, if you first apply the first ones then the middle ones and then the last ones. Obviously if there are dependencies between the patches that will put a constraint on how you can choose to divide them up. The PatchChoices data type and associated functions are here to deal with many of the common cases that come up when choosing a subset of a group of patches. force_last tells PatchChoices that a particular patch is required to be in the ``last'' group, which also means that any patches that depend on it must be in the ``last'' group. Internally, a PatchChoices doesn't actually reorder the patches until it is asked for the final output (e.g.\ via get_first_choice). Instead, each patch is placed in a state of definitely first, definitely last and undecided--undecided leans towards ``middle''. In case you're wondering about the first-middle-last language, it's because in some cases the ``yes'' answers will be last (as is the case for the revert command), and in others first (as in record, pull and push). \begin{code} newtype PatchChoices = PC [(Patch, Maybe Bool, Int)] type EasyPC = [(Patch, Maybe Bool, Int)] patch_choices :: [Patch] -> PatchChoices patch_choices ps = PC $ zip3 ps (repeat Nothing) [1..] get_first_choice :: PatchChoices -> [Patch] get_last_choice :: PatchChoices -> [Patch] get_middle_choice :: PatchChoices -> [Patch] force_matching_first :: (Patch -> Bool) -> PatchChoices -> PatchChoices force_matching_last :: (Patch -> Bool) -> PatchChoices -> PatchChoices force_firsts :: [Patch] -> PatchChoices -> PatchChoices force_first :: Patch -> PatchChoices -> PatchChoices force_lasts :: [Patch] -> PatchChoices -> PatchChoices force_last :: Patch -> PatchChoices -> PatchChoices make_uncertain :: Patch -> PatchChoices -> PatchChoices make_everything_later :: PatchChoices -> PatchChoices is_patch_first :: Patch -> PatchChoices -> Maybe Bool \end{code} \begin{code} reverse_easy :: EasyPC -> EasyPC reverse_easy xs = reverse $ map (\(p,mf,n) -> (invert p,not `liftM` mf,n)) xs reverse_patches :: [Patch] -> [Patch] reverse_patches ps = reverse $ map invert ps get_first_choice (PC e) = fst $ pull_firsts e get_last_choice (PC e) = reverse_patches $ fst $ pull_firsts $ reverse_easy e get_middle_choice (PC e) = map fst_o_3 $ reverse_easy $ snd $ pull_firsts $ reverse_easy $ snd $ pull_firsts e where fst_o_3 (a,_,_) = a pull_firsts :: EasyPC -> ([Patch], EasyPC) pull_firsts e = case pull_first e of Nothing -> ([],e) Just (p,e') -> case pull_firsts e' of (ps,e'') -> (p:ps,e'') pull_lasts :: EasyPC -> ([Patch], EasyPC) pull_lasts e = case pull_firsts $ reverse_easy e of (ps,e') -> (reverse ps, reverse_easy e') pull_first :: EasyPC -> Maybe (Patch, EasyPC) pull_first [] = Nothing pull_first ((p,Just True,_):e) = Just (p,e) pull_first ((p,Just False,n):e) = case pull_first e of Just (p2,e') -> case commute (p2,p) of Just (p',p2') -> Just (p2',((p',Just False,n):e')) Nothing -> error "Aaack fixme!" Nothing -> Nothing pull_first ((p,Nothing,n):e) = case pull_first e of Just (p2,e') -> case commute (p2,p) of Just (p',p2') -> Just (p2',((p',Nothing,n):e')) Nothing -> Just (p,((p2,Just True,-1):e')) Nothing -> Nothing \end{code} \begin{code} is_patch_first p (PC e) = ipf e where ipf ((a,mb,_):e') | a == p = mb | otherwise = ipf e' ipf [] = error "Aaack in ipf! Please report this bug." set_simplys :: [Int] -> Bool -> EasyPC -> EasyPC set_simplys xs b e = map ch e where ch (p,_,n) | n `elem` xs = (p,Just b,n) | otherwise = (p,Nothing,n) m2ids :: (Patch -> Bool) -> EasyPC -> [Int] m2ids m ((a,_,pid):e) | m a = pid : m2ids m e | otherwise = m2ids m e m2ids _ [] = [] force_matching_first m (PC e) = let thd (_,_,t) = t xs = m2ids m e not_needed = map thd $ snd $ pull_firsts $ set_simplys xs True e ch (p,mb,n) | n `elem` not_needed = (p,mb,n) | otherwise = (p,Just True, n) in PC $ map ch e force_firsts ps pc = force_matching_first (`elem` ps) pc force_first p pc = force_matching_first (== p) pc reverse_pc :: PatchChoices -> PatchChoices reverse_pc (PC e) = PC $ reverse_easy e force_matching_last m (PC e) = let thd (_,_,t) = t xs = m2ids m e not_needed = map thd $ snd $ pull_lasts $ set_simplys xs False e ch (p,mb,n) | n `elem` not_needed = (p,mb,n) | otherwise = (p,Just False, n) in PC $ map ch e force_last p pc = reverse_pc $ force_first (invert p) $ reverse_pc pc force_lasts ps pc = reverse_pc $ force_firsts (map invert ps) $ reverse_pc pc make_uncertain p (PC e) = PC $ map ch e where ch (a,mb,n) = if a == p then (a,Nothing,n) else (a,mb,n) make_everything_later (PC e) = PC $ map ch e where ch (a,Nothing,n) = (a,Just False,n) ch (a,Just True,n) = (a,Nothing,n) ch x = x \end{code}