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)
28newtype 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 = Map.elems . patchMapWithMoveNewElementsMap
67patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
68patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
69 where f ni = case _nodeInfo_from ni of
70 From_Insert v -> Just v
71 From_Move _ -> Nothing
72 From_Delete -> Nothing
74validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
75validPatchMapWithMove (PatchMapWithMove p) =
76 let f ni = case _nodeInfo_from ni of
79 moveSources = catMaybes $ f <$> Map.elems p
81 [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
82 all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
83 , -- 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)
84 all (isJust . flip Map.lookup p) moveSources
87knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
88knownValidPatchMapWithMove =
89 [ PatchMapWithMove mempty
90 , PatchMapWithMove $ Map.fromList
91 [ (1, NodeInfo (From_Move 2) $ Just 1)
92 , (2, NodeInfo (From_Move 1) $ Just 2)
96knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
97knownInvalidPatchMapWithMove =
98 [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
99 PatchMapWithMove $ Map.singleton 1 $ NodeInfo (From_Move 2) Nothing
100 , -- Invalid because we have multiple moves from 3
101 PatchMapWithMove $ Map.fromList
102 [ (1, NodeInfo (From_Move 3) Nothing)
103 , (2, NodeInfo (From_Move 3) Nothing)
104 , (3, NodeInfo From_Delete (Just 1))
108patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
109patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
110 where unsorted = Map.toList m
111 sorted = sortBy (cmp `on` snd) unsorted
112 f (to, _) (from, _) = if to == from then Nothing else
114 reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
115 g (to, _) (from, _) = if to == from then Nothing else
116 let Just movingTo = Map.lookup from reverseMapping
117 in Just (to, NodeInfo (From_Move from) $ Just movingTo)
119patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
120patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
121 where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
122 newList = Map.toList newByIndexUnsorted
123 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
124 insertsAndMoves :: Map k (NodeInfo k v)
125 (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
127 remainingValues <- get
128 let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
129 then Map.delete v remainingValues
130 else Map.insert v remainingKeys remainingValues
131 case Map.lookup v remainingValues of
132 Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
134 if k `Set.member` fromKs
136 putRemainingKeys $ Set.delete k fromKs
137 return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
139 Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
140 putRemainingKeys remainingKeys
141 return $ NodeInfo (From_Move fromK) $ Just undefined
142 Map.traverseWithKey f newByIndex
143 unusedOldKeys = fold unusedValuesByValue
144 pointlessMove k = \case
145 From_Move k' | k == k' -> True
147 keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
150 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
152nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
153nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
155nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
156nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
158nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
159nodeInfoSetTo 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