mirror of
https://github.com/github/semantic.git
synced 2024-12-26 00:12:29 +03:00
200 lines
8.6 KiB
Haskell
200 lines
8.6 KiB
Haskell
{-# LANGUAGE DataKinds, RankNTypes, TypeFamilies, TypeOperators, ScopedTypeVariables, UndecidableInstances #-}
|
||
module Data.Diff
|
||
( Diff(..)
|
||
, DiffF(..)
|
||
, comparing
|
||
, compareF
|
||
, inserting
|
||
, insertF
|
||
, deleting
|
||
, deleteF
|
||
, merge
|
||
, mergeF
|
||
, merging
|
||
, diffPatches
|
||
) where
|
||
|
||
import Data.Aeson
|
||
import Data.Bifoldable
|
||
import Data.Bifunctor
|
||
import Data.Bitraversable
|
||
import Data.Edit
|
||
import Data.Functor.Classes
|
||
import Data.Functor.Foldable
|
||
import Data.JSON.Fields
|
||
import Data.Term
|
||
import Text.Show
|
||
|
||
-- | A recursive structure indicating the changed & unchanged portions of a labelled tree.
|
||
newtype Diff syntax ann1 ann2 = Diff { unDiff :: DiffF syntax ann1 ann2 (Diff syntax ann1 ann2) }
|
||
|
||
-- | A single entry within a recursive 'Diff'.
|
||
data DiffF syntax ann1 ann2 recur
|
||
-- | A changed node, represented as 'Insert'ed, 'Delete'd, or 'Compare'd 'TermF's, consisting of syntax labelled with an annotation.
|
||
= Patch (Edit (TermF syntax ann1 recur)
|
||
(TermF syntax ann2 recur))
|
||
-- | An unchanged node, consisting of syntax labelled with both the original annotations.
|
||
| Merge (TermF syntax (ann1, ann2) recur)
|
||
|
||
-- | Constructs a 'Diff' comparing one 'Term' with another recursively.
|
||
comparing :: Functor syntax => Term syntax ann1 -> Term syntax ann2 -> Diff syntax ann1 ann2
|
||
comparing (Term (In a1 r1)) (Term (In a2 r2)) = compareF (In a1 (deleting <$> r1)) (In a2 (inserting <$> r2))
|
||
|
||
-- | Constructs a 'Diff' comparing one 'TermF' populated by further 'Diff's with another.
|
||
compareF :: TermF syntax ann1 (Diff syntax ann1 ann2) -> TermF syntax ann2 (Diff syntax ann1 ann2) -> Diff syntax ann1 ann2
|
||
compareF t1 t2 = Diff (Patch (Compare t1 t2))
|
||
|
||
-- | Constructs a 'Diff' inserting a 'Term' recursively.
|
||
inserting :: Functor syntax => Term syntax ann2 -> Diff syntax ann1 ann2
|
||
inserting = cata insertF
|
||
|
||
-- | Constructs a 'Diff' inserting a single 'TermF' populated by further 'Diff's.
|
||
insertF :: TermF syntax ann2 (Diff syntax ann1 ann2) -> Diff syntax ann1 ann2
|
||
insertF = Diff . Patch . Insert
|
||
|
||
-- | Constructs a 'Diff' deleting a 'Term' recursively.
|
||
deleting :: Functor syntax => Term syntax ann1 -> Diff syntax ann1 ann2
|
||
deleting = cata deleteF
|
||
|
||
-- | Constructs a 'Diff' deleting a single 'TermF' populated by further 'Diff's.
|
||
deleteF :: TermF syntax ann1 (Diff syntax ann1 ann2) -> Diff syntax ann1 ann2
|
||
deleteF = Diff . Patch . Delete
|
||
|
||
-- | Constructs a 'Diff' merging two annotations for a single syntax functor populated by further 'Diff's.
|
||
merge :: (ann1, ann2) -> syntax (Diff syntax ann1 ann2) -> Diff syntax ann1 ann2
|
||
merge = fmap mergeF . In
|
||
|
||
-- | Constructs a 'Diff' merging a single 'TermF' populated by further 'Diff's.
|
||
mergeF :: TermF syntax (ann1, ann2) (Diff syntax ann1 ann2) -> Diff syntax ann1 ann2
|
||
mergeF = Diff . Merge
|
||
|
||
-- | Constructs a 'Diff' merging a 'Term' recursively.
|
||
--
|
||
-- Note that since this simply duplicates the 'Term'’s annotations, it is only really useful in tests or other contexts where preserving annotations from both sides is unnecessary.
|
||
merging :: Functor syntax => Term syntax ann -> Diff syntax ann ann
|
||
merging = cata (\ (In ann syntax) -> mergeF (In (ann, ann) syntax))
|
||
|
||
|
||
diffPatches :: (Foldable syntax, Functor syntax) => Diff syntax ann1 ann2 -> [Edit (TermF syntax ann1 (Diff syntax ann1 ann2)) (TermF syntax ann2 (Diff syntax ann1 ann2))]
|
||
diffPatches = para $ \ diff -> case diff of
|
||
Patch patch -> bimap (fmap fst) (fmap fst) patch : bifoldMap (foldMap snd) (foldMap snd) patch
|
||
Merge merge -> foldMap snd merge
|
||
|
||
|
||
type instance Base (Diff syntax ann1 ann2) = DiffF syntax ann1 ann2
|
||
|
||
instance Functor syntax => Recursive (Diff syntax ann1 ann2) where project = unDiff
|
||
instance Functor syntax => Corecursive (Diff syntax ann1 ann2) where embed = Diff
|
||
|
||
|
||
instance Eq1 syntax => Eq2 (Diff syntax) where
|
||
liftEq2 eq1 eq2 = go where go (Diff d1) (Diff d2) = liftEq3 eq1 eq2 go d1 d2
|
||
|
||
instance (Eq1 syntax, Eq ann1, Eq ann2) => Eq (Diff syntax ann1 ann2) where
|
||
(==) = eq2
|
||
|
||
instance Eq1 syntax => Eq3 (DiffF syntax) where
|
||
liftEq3 eq1 eq2 eqRecur d1 d2 = case (d1, d2) of
|
||
(Patch p1, Patch p2) -> liftEq2 (liftEq2 eq1 eqRecur) (liftEq2 eq2 eqRecur) p1 p2
|
||
(Merge t1, Merge t2) -> liftEq2 (liftEq2 eq1 eq2) eqRecur t1 t2
|
||
_ -> False
|
||
|
||
instance (Eq1 syntax, Eq ann1, Eq ann2) => Eq1 (DiffF syntax ann1 ann2) where
|
||
liftEq = liftEq3 (==) (==)
|
||
|
||
instance (Eq1 syntax, Eq ann1, Eq ann2, Eq recur) => Eq (DiffF syntax ann1 ann2 recur) where
|
||
(==) = eq3
|
||
|
||
|
||
instance Show1 syntax => Show2 (Diff syntax) where
|
||
liftShowsPrec2 sp1 sl1 sp2 sl2 = go where go d = showsUnaryWith (liftShowsPrec3 sp1 sl1 sp2 sl2 go (showListWith (go 0))) "Diff" d . unDiff
|
||
|
||
instance (Show1 syntax, Show ann1, Show ann2) => Show (Diff syntax ann1 ann2) where
|
||
showsPrec = showsPrec2
|
||
|
||
instance Show1 syntax => Show3 (DiffF syntax) where
|
||
liftShowsPrec3 sp1 sl1 sp2 sl2 spRecur slRecur d diff = case diff of
|
||
Patch patch -> showsUnaryWith (liftShowsPrec2 (liftShowsPrec2 sp1 sl1 spRecur slRecur) (liftShowList2 sp1 sl1 spRecur slRecur) (liftShowsPrec2 sp2 sl2 spRecur slRecur) (liftShowList2 sp2 sl2 spRecur slRecur)) "Patch" d patch
|
||
Merge term -> showsUnaryWith (liftShowsPrec2 spBoth slBoth spRecur slRecur) "Merge" d term
|
||
where spBoth = liftShowsPrec2 sp1 sl1 sp2 sl2
|
||
slBoth = liftShowList2 sp1 sl1 sp2 sl2
|
||
|
||
instance (Show1 syntax, Show ann1, Show ann2) => Show1 (DiffF syntax ann1 ann2) where
|
||
liftShowsPrec = liftShowsPrec3 showsPrec showList showsPrec showList
|
||
|
||
instance (Show1 syntax, Show ann1, Show ann2, Show recur) => Show (DiffF syntax ann1 ann2 recur) where
|
||
showsPrec = showsPrec3
|
||
|
||
instance Functor syntax => Bifunctor (Diff syntax) where
|
||
bimap f g = go where go = Diff . trimap f g go . unDiff
|
||
|
||
instance Foldable syntax => Bifoldable (Diff syntax) where
|
||
bifoldMap f g = go where go = trifoldMap f g go . unDiff
|
||
|
||
instance Traversable syntax => Bitraversable (Diff syntax) where
|
||
bitraverse f g = go where go = fmap Diff . tritraverse f g go . unDiff
|
||
|
||
|
||
instance Functor syntax => Functor (DiffF syntax ann1 ann2) where
|
||
fmap = trimap id id
|
||
|
||
instance Functor syntax => Trifunctor (DiffF syntax) where
|
||
trimap f g h (Patch patch) = Patch (bimap (bimap f h) (bimap g h) patch)
|
||
trimap f g h (Merge term) = Merge (bimap (bimap f g) h term)
|
||
|
||
instance Foldable syntax => Foldable (DiffF syntax ann1 ann2) where
|
||
foldMap = trifoldMap (const mempty) (const mempty)
|
||
|
||
instance Foldable syntax => Trifoldable (DiffF syntax) where
|
||
trifoldMap f g h (Patch patch) = bifoldMap (bifoldMap f h) (bifoldMap g h) patch
|
||
trifoldMap f g h (Merge term) = bifoldMap (bifoldMap f g) h term
|
||
|
||
instance Traversable syntax => Traversable (DiffF syntax ann1 ann2) where
|
||
traverse = tritraverse pure pure
|
||
|
||
instance Traversable syntax => Tritraversable (DiffF syntax) where
|
||
tritraverse f g h (Patch patch) = Patch <$> bitraverse (bitraverse f h) (bitraverse g h) patch
|
||
tritraverse f g h (Merge term) = Merge <$> bitraverse (bitraverse f g) h term
|
||
|
||
|
||
instance (ToJSONFields1 syntax, ToJSONFields ann1, ToJSONFields ann2) => ToJSON (Diff syntax ann1 ann2) where
|
||
toJSON = object . toJSONFields
|
||
toEncoding = pairs . mconcat . toJSONFields
|
||
|
||
instance (ToJSONFields1 syntax, ToJSONFields ann1, ToJSONFields ann2) => ToJSONFields (Diff syntax ann1 ann2) where
|
||
toJSONFields = toJSONFields . unDiff
|
||
|
||
instance (ToJSONFields1 syntax, ToJSONFields ann1, ToJSONFields ann2) => ToJSONFields1 (DiffF syntax ann1 ann2) where
|
||
toJSONFields1 (Patch patch) = [ "patch" .= JSONFields patch ]
|
||
toJSONFields1 (Merge term) = [ "merge" .= JSONFields term ]
|
||
|
||
instance (ToJSONFields1 syntax, ToJSONFields ann1, ToJSONFields ann2, ToJSON recur) => ToJSONFields (DiffF syntax ann1 ann2 recur) where
|
||
toJSONFields = toJSONFields1
|
||
|
||
instance (ToJSONFields1 syntax, ToJSONFields ann1, ToJSONFields ann2, ToJSON recur) => ToJSON (DiffF syntax ann1 ann2 recur) where
|
||
toJSON = object . toJSONFields
|
||
toEncoding = pairs . mconcat . toJSONFields
|
||
|
||
|
||
class Eq3 f where
|
||
liftEq3 :: (a1 -> a2 -> Bool) -> (b1 -> b2 -> Bool) -> (c1 -> c2 -> Bool) -> f a1 b1 c1 -> f a2 b2 c2 -> Bool
|
||
|
||
eq3 :: (Eq3 f, Eq a, Eq b, Eq c) => f a b c -> f a b c -> Bool
|
||
eq3 = liftEq3 (==) (==) (==)
|
||
|
||
|
||
class Show3 f where
|
||
liftShowsPrec3 :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> (Int -> b -> ShowS) -> ([b] -> ShowS) -> (Int -> c -> ShowS) -> ([c] -> ShowS) -> Int -> f a b c -> ShowS
|
||
|
||
showsPrec3 :: (Show3 f, Show a, Show b, Show c) => Int -> f a b c -> ShowS
|
||
showsPrec3 = liftShowsPrec3 showsPrec showList showsPrec showList showsPrec showList
|
||
|
||
class Trifunctor f where
|
||
trimap :: (a -> a') -> (b -> b') -> (c -> c') -> f a b c -> f a' b' c'
|
||
|
||
class Trifoldable f where
|
||
trifoldMap :: Monoid m => (a -> m) -> (b -> m) -> (c -> m) -> f a b c -> m
|
||
|
||
class Tritraversable f where
|
||
tritraverse :: Applicative g => (a -> g a') -> (b -> g b') -> (c -> g c') -> f a b c -> g (f a' b' c')
|