summaryrefslogtreecommitdiff
path: root/Annex/View.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2014-02-19 15:10:18 -0400
committerGravatar Joey Hess <joey@kitenet.net>2014-02-19 16:29:56 -0400
commiteaf19cc8c9be6e53c0ae7a1fba48a947c41d2ee6 (patch)
treebac64b7fde8afb83661ac5384a2181cb91bd0926 /Annex/View.hs
parent22b8d9c7603d6ed610ed9bbc3e59dbdb39e885c5 (diff)
add tip about metadata driven views (and more flexible view filtering)
While writing this documentation, I realized that there needed to be a way to stay in a view like tag=* while adding a filter like tag=work that applies to the same field. So, there are really two ways a view can be refined. It can have a new "field=explicitvalue" filter added to it, which does not change the "shape" of the view, but narrows the files it shows. Or, it can have a new view added, which adds another level of subdirectories. So, added a vfilter command, which takes explicit values to add to the filter, and rejects changes that would change the shape of the view. And, made vadd only accept changes that change the shape of the view. And, changed the View data type slightly; now components that can match multiple metadata values can be visible, or not visible. This commit was sponsored by Stelian Iancu.
Diffstat (limited to 'Annex/View.hs')
-rw-r--r--Annex/View.hs62
1 files changed, 42 insertions, 20 deletions
diff --git a/Annex/View.hs b/Annex/View.hs
index abf8f073e..78b4da589 100644
--- a/Annex/View.hs
+++ b/Annex/View.hs
@@ -44,11 +44,7 @@ import Text.Regex.TDFA.String
import Text.Regex
#endif
-
-data ViewChange = Unchanged | Narrowing | Widening
- deriving (Ord, Eq, Show)
-
-{- Each multivalued ViewFilter in a view results in another level of
+{- Each visible ViewFilter in a view results in another level of
- subdirectory nesting. When a file matches multiple ways, it will appear
- in multiple subdirectories. This means there is a bit of an exponential
- blowup with a single file appearing in a crazy number of places!
@@ -60,16 +56,38 @@ viewTooLarge :: View -> Bool
viewTooLarge view = visibleViewSize view > 5
visibleViewSize :: View -> Int
-visibleViewSize = length . filter (multiValue . viewFilter) . viewComponents
+visibleViewSize = length . filter viewVisible . viewComponents
-{- 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
+data ViewChange = Unchanged | Narrowing | Widening
+ deriving (Ord, Eq, Show)
+
+{- Updates a view, adding new fields to filter on (Narrowing),
+ - or allowing new values in an existing field (Widening). -}
+refineView :: View -> [(MetaField, String)] -> (View, ViewChange)
+refineView = go Unchanged
+ where
+ go c v [] = (v, c)
+ go c v ((f, s):rest) =
+ let (v', c') = refineView' v f s
+ in go (max c c') v' rest
+
+{- Adds an additional filter to a view. This can only result in narrowing
+ - the view. Multivalued filters are added in non-visible form. -}
+filterView :: View -> [(MetaField, String)] -> View
+filterView v vs = v { viewComponents = viewComponents f' ++ viewComponents v}
+ where
+ f = fst $ refineView (v {viewComponents = []}) vs
+ f' = f { viewComponents = map toinvisible (viewComponents f) }
+ toinvisible c = c { viewVisible = False }
+
+refineView' :: View -> MetaField -> String -> (View, ViewChange)
+refineView' view field wanted
| field `elem` (map viewField components) =
let (components', viewchanges) = runWriter $ mapM updatefield components
in (view { viewComponents = components' }, maximum viewchanges)
- | otherwise = let view' = view { viewComponents = ViewComponent field viewfilter : components }
+ | otherwise =
+ let component = ViewComponent field viewfilter (multiValue viewfilter)
+ view' = view { viewComponents = component : components }
in if viewTooLarge view'
then error $ "View is too large (" ++ show (visibleViewSize view') ++ " levels of subdirectories)"
else (view', Narrowing)
@@ -173,8 +191,8 @@ fileViews view =
else map (</> mkfileview file) paths
where
visible = map (fromJust . snd) .
- filter (multiValue . fst) .
- zip (map viewFilter (viewComponents view))
+ filter (viewVisible . fst) .
+ zip (viewComponents view)
{- Checks if metadata matches a ViewComponent filter, and if so
- returns the value, or values that match. Self-memoizing on ViewComponent. -}
@@ -255,7 +273,7 @@ pathProduct (l:ls) = foldl combinel l ls
fromView :: View -> FileView -> MetaData
fromView view f = foldr (uncurry updateMetaData) newMetaData (zip fields values)
where
- visible = filter (multiValue . viewFilter) (viewComponents view)
+ visible = filter viewVisible (viewComponents view)
fields = map viewField visible
paths = splitDirectories $ dropFileName f
values = map fromViewPath paths
@@ -263,15 +281,15 @@ fromView view f = foldr (uncurry updateMetaData) newMetaData (zip fields values)
{- Constructing a view that will match arbitrary metadata, and applying
- it to a file yields a set of FileViews which all contain the same
- MetaFields that were present in the input metadata
- - (excluding fields that are not multivalued). -}
-prop_view_roundtrips :: FilePath -> MetaData -> Bool
-prop_view_roundtrips f metadata = null f || viewTooLarge view ||
+ - (excluding fields that are not visible). -}
+prop_view_roundtrips :: FilePath -> MetaData -> Bool -> Bool
+prop_view_roundtrips f metadata visible = null f || viewTooLarge view ||
all hasfields (fileViews view fileViewFromReference f metadata)
where
view = View (Git.Ref "master") $
- map (\(mf, mv) -> ViewComponent mf (FilterValues $ S.filter (not . null . fromMetaValue) mv))
+ map (\(mf, mv) -> ViewComponent mf (FilterValues $ S.filter (not . null . fromMetaValue) mv) visible)
(fromMetaData metadata)
- visiblefields = sort (map viewField $ filter (multiValue . viewFilter) (viewComponents view))
+ visiblefields = sort (map viewField $ filter viewVisible (viewComponents view))
hasfields fv = sort (map fst (fromMetaData (fromView view fv))) == visiblefields
{- Applies a view to the currently checked out branch, generating a new
@@ -282,7 +300,8 @@ applyView view = applyView' fileViewFromReference view
{- 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.
+ - checked out branch. That is, it must match a subset of the files
+ - in view, not any others.
-}
narrowView :: View -> Annex Git.Branch
narrowView = applyView' fileViewReuse
@@ -405,3 +424,6 @@ withIndex :: Annex a -> Annex a
withIndex a = do
f <- fromRepo gitAnnexViewIndex
withIndexFile f a
+
+withCurrentView :: (View -> Annex a) -> Annex a
+withCurrentView a = maybe (error "Not in a view.") a =<< currentView