diff options
author | 2014-02-16 22:44:28 -0400 | |
---|---|---|
committer | 2014-02-16 22:44:28 -0400 | |
commit | d2455918de02e407c463597d4656aeb9adb59010 (patch) | |
tree | d0fe51712df3ca31b125d6e6b9e0ad67057a5a81 /Annex | |
parent | 40cb42468af4661d8b9e0a59e00e5be70691f24e (diff) |
tricky view refining code that keeps track of whether the view is widenening or narrowing
Diffstat (limited to 'Annex')
-rw-r--r-- | Annex/View.hs | 87 |
1 files changed, 76 insertions, 11 deletions
diff --git a/Annex/View.hs b/Annex/View.hs index d05256752..aef9e0a66 100644 --- a/Annex/View.hs +++ b/Annex/View.hs @@ -22,12 +22,13 @@ import Utility.QuickCheck import qualified Data.Set as S import Data.Char +import System.Path.WildMatch +import "mtl" Control.Monad.Writer #ifdef WITH_TDFA import Text.Regex.TDFA import Text.Regex.TDFA.String #else -import System.Path.WildMatch #endif type View = [(MetaField, ViewFilter)] @@ -66,6 +67,76 @@ getGlob (Glob g _) = g getGlob (Glob g) = g #endif +matchGlob :: Glob -> String -> Bool +#ifdef WITH_TDFA +matchGlob (Glob _ r) s = case execute r s of + Right (Just _) -> True + _ -> False +#else +matchGlob (Glob g) = wildCheckCase g +#endif + +data ViewChange = Unchanged | Narrowing | Widening + deriving (Ord, Eq, Show) + +{- Updates a view, adding a new field to filter on (Narrowing), + - or allowing a new value in an existing field (Widening). + -} +refineView :: View -> MetaField -> String -> (View, ViewChange) +refineView view field wanted + | field `elem` (map fst view) = + let (view', viewchanges) = runWriter $ mapM updatefield view + in (view', maximum viewchanges) + | otherwise = ((field, viewfilter) : view, Narrowing) + where + viewfilter + | any (`elem` wanted) "*?" = +#ifdef WITH_TDFA + case compile defaultCompOpt defaultExecOpt ('^':wildToRegex wanted) of + Right r -> FilterGlob (Glob wanted r) + Left _ -> FilterValues $ S.singleton $ toMetaValue wanted +#else + FilterGlob (Glob wanted) +#endif + | otherwise = FilterValues $ S.singleton $ toMetaValue wanted + updatefield :: (MetaField, ViewFilter) -> Writer [ViewChange] (MetaField, ViewFilter) + updatefield v@(f, vf) + | f == field = do + let (newvf, viewchange) = combineViewFilter vf viewfilter + tell [viewchange] + return (f, newvf) + | otherwise = return v + +{- Combine old and new ViewFilters, yielding a results that matches + - either old+new, or only new. + - + - If we have FilterValues and change to a FilterGlob, + - it's always a widening change, because the glob could match other + - values. OTOH, going the other way, it's a Narrowing change if the old + - glob matches all the new FilterValues. + - + - With two globs, the old one is discarded, and the new one is used. + - We can tell if that's a narrowing change by checking if the old + - glob matches the new glob. For example, "*" matches "foo*", + - so that's narrowing. While "f?o" does not match "f??", so that's + - widening. + -} +combineViewFilter :: ViewFilter -> ViewFilter -> (ViewFilter, ViewChange) +combineViewFilter old@(FilterValues olds) (FilterValues news) + | combined == old = (combined, Unchanged) + | otherwise = (combined, Widening) + where + combined = FilterValues (S.union olds news) +combineViewFilter (FilterValues old) newglob@(FilterGlob _) = + (newglob, Widening) +combineViewFilter (FilterGlob oldglob) new@(FilterValues s) + | all (matchGlob oldglob . fromMetaValue) (S.toList s) = (new, Narrowing) + | otherwise = (new, Widening) +combineViewFilter (FilterGlob old) newglob@(FilterGlob new) + | old == new = (newglob, Unchanged) + | matchGlob old (getGlob new) = (newglob, Narrowing) + | otherwise = (newglob, Widening) + {- Can a ViewFilter match multiple different MetaValues? -} multiValue :: ViewFilter -> Bool multiValue (FilterValues s) = S.size s > 1 @@ -91,13 +162,7 @@ matchFilter :: MetaData -> MetaField -> ViewFilter -> Maybe [MetaValue] matchFilter metadata metafield (FilterValues s) = nonEmptyList $ S.intersection s (currentMetaDataValues metafield metadata) matchFilter metadata metafield (FilterGlob glob) = nonEmptyList $ - S.filter (matching glob . fromMetaValue) (currentMetaDataValues metafield metadata) - where -#ifdef WITH_TDFA - matching (Glob _ r) = either (const False) (const True) . execute r -#else - matching (Glob g) = wildCheckCase g -#endif + S.filter (matchGlob glob . fromMetaValue) (currentMetaDataValues metafield metadata) nonEmptyList :: S.Set a -> Maybe [a] nonEmptyList s @@ -249,12 +314,12 @@ prop_branchView_legal = Git.Ref.legal False . show . branchView applyView :: View -> Annex Git.Branch applyView = applyView' fileViewFromReference -{- Generates a new branch for a View, which must be a more specific +{- Generates a new branch for a View, which must be a more narrow - version of the View originally used to generate the currently - checked out branch. -} -refineView :: View -> Annex Git.Branch -refineView = applyView' id +narrowView :: View -> Annex Git.Branch +narrowView = applyView' id {- Go through each file in the currently checked out branch. - If the file is not annexed, skip it, unless it's a dotfile in the top. |