1{-# LANGUAGE DeriveFoldable #-}
2{-# LANGUAGE DeriveFunctor #-}
3{-# LANGUAGE DeriveTraversable #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
8module Reflex.Patch.MapWithMove where
10import Reflex.Patch.Class
13import Control.Monad.State
18import qualified Data.Map as Map
20import qualified Data.Set as Set
23-- | Patch a DMap with additions, deletions, and moves.
25-- * The same source key may not be moved to two destination keys.
26-- * Any key that is the source of a Move must also be edited
27-- (otherwise, the Move would duplicate it)
28data PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
30data NodeInfo k v = NodeInfo
31 { _nodeInfo_from :: !(From k v)
32 , _nodeInfo_to :: !(To k)
34 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
36unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
37unPatchMapWithMove (PatchMapWithMove p) = p
39-- | Warning: when using this function, you must ensure that the invariants of
40-- 'PatchMapWithMove' are preserved; they will not be checked.
41unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
42unsafePatchMapWithMove = PatchMapWithMove
45 = From_Insert v -- ^ Insert the given value here
46 | From_Delete -- ^ Delete the existing value, if any, from here
47 | From_Move !k -- ^ Move the value here from the given key
48 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
52instance Ord k => Patch (PatchMapWithMove k v) where
53 type PatchTarget (PatchMapWithMove k v) = Map k v
54 apply (PatchMapWithMove p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
55 where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
56 From_Insert v -> Just v
57 From_Move k -> Map.lookup k old
58 From_Delete -> Nothing
59 deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
60 From_Delete -> Just ()
63-- | Returns all the new elements that will be added to the 'Map'
64patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
65patchMapWithMoveNewElements (PatchMapWithMove p) = catMaybes $ f <$> Map.elems p
66 where f ni = case _nodeInfo_from ni of
67 From_Insert v -> Just v
68 From_Move _ -> Nothing
69 From_Delete -> Nothing
71validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
72validPatchMapWithMove (PatchMapWithMove p) =
73 let f ni = case _nodeInfo_from ni of
76 moveSources = catMaybes $ f <$> Map.elems p
78 [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
79 all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
80 , -- All Move source keys must also be patched - since the value's being moved elsewhere, the original must either be Deleted or replaced (with Insert or Move)
81 all (isJust . flip Map.lookup p) moveSources
84knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
85knownValidPatchMapWithMove =
86 [ PatchMapWithMove mempty
87 , PatchMapWithMove $ Map.fromList
88 [ (1, NodeInfo (From_Move 2) $ Just 1)
89 , (2, NodeInfo (From_Move 1) $ Just 2)
93knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
94knownInvalidPatchMapWithMove =
95 [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
96 PatchMapWithMove $ Map.singleton 1 $ NodeInfo (From_Move 2) Nothing
97 , -- Invalid because we have multiple moves from 3
98 PatchMapWithMove $ Map.fromList
99 [ (1, NodeInfo (From_Move 3) Nothing)
100 , (2, NodeInfo (From_Move 3) Nothing)
101 , (3, NodeInfo From_Delete (Just 1))
105patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
106patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
107 where unsorted = Map.toList m
108 sorted = sortBy (cmp `on` snd) unsorted
109 f (to, _) (from, _) = if to == from then Nothing else
111 reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
112 g (to, _) (from, _) = if to == from then Nothing else
113 let Just movingTo = Map.lookup from reverseMapping
114 in Just (to, NodeInfo (From_Move from) $ Just movingTo)
116patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
117patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
118 where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
119 newList = Map.toList newByIndexUnsorted
120 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
121 insertsAndMoves :: Map k (NodeInfo k v)
122 (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
124 remainingValues <- get
125 let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
126 then Map.delete v remainingValues
127 else Map.insert v remainingKeys remainingValues
128 case Map.lookup v remainingValues of
129 Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
131 if k `Set.member` fromKs
133 putRemainingKeys $ Set.delete k fromKs
134 return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
136 Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
137 putRemainingKeys remainingKeys
138 return $ NodeInfo (From_Move fromK) $ Just undefined
139 Map.traverseWithKey f newByIndex
140 unusedOldKeys = fold unusedValuesByValue
141 pointlessMove k = \case
142 From_Move k' | k == k' -> True
144 keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
147 patch = unsafePatchMapWithMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
149nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
150nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
152nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
153nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
155nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
156nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
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