From 33270bd65aaa9b2a42d700a5aa74cff089c0ec75 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Apr 2016 11:46:10 -0400 Subject: [PATCH 01/53] stub diffSummary --- semantic-diff.cabal | 1 + src/DiffSummary.hs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/DiffSummary.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index bcc76d21d..cf3afb1df 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -47,6 +47,7 @@ library , Syntax , Term , TreeSitter + , DiffSummary build-depends: aeson , base >= 4.8 && < 5 , blaze-html diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs new file mode 100644 index 000000000..006c107be --- /dev/null +++ b/src/DiffSummary.hs @@ -0,0 +1,39 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +module DiffSummary where + +import Diff +import Info +import Patch +import Term +import Syntax +import Data.Monoid + +newtype DiffSummary = DiffSummary { diffChanges :: [DiffChange] } + deriving (Monoid) + +emptyDiffSummary :: DiffSummary +emptyDiffSummary = DiffSummary { diffChanges = [] } + +data DiffChange = Insertion { category :: String, termName :: String } + + +patchSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary +patchSummary termSummary patch = maybe emptyDiffSummary termSummary beforeTerm <> + maybe emptyDiffSummary termSummary afterTerm + where + beforeTerm = before patch + afterTerm = after patch + +diffSummary :: Diff (Patch a) Info -> DiffSummary +diffSummary = foldMap (patchSummary termSummary) +-- $ fmap (patchSummary termSummary) diff-- diffSum $ patchSummary diff +-- (a -> b -> b) -> b -> t a -> b + +termSummary :: Term (Patch a) Info -> DiffSummary +termSummary = cata summary where + summary _ (Leaf (Replace _ _)) = emptyDiffSummary + summary _ (Leaf (Insert _)) = emptyDiffSummary + summary _ (Leaf (Delete _)) = emptyDiffSummary + summary _ (Indexed _) = emptyDiffSummary + summary _ (Fixed _) = emptyDiffSummary + summary _ (Keyed _) = emptyDiffSummary From 305371e6c86f1f8789c7a87dfa5fc6be2ecdae15 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Apr 2016 11:50:56 -0400 Subject: [PATCH 02/53] no comment --- src/DiffSummary.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 006c107be..5428dbe77 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -26,8 +26,6 @@ patchSummary termSummary patch = maybe emptyDiffSummary termSummary beforeTerm < diffSummary :: Diff (Patch a) Info -> DiffSummary diffSummary = foldMap (patchSummary termSummary) --- $ fmap (patchSummary termSummary) diff-- diffSum $ patchSummary diff --- (a -> b -> b) -> b -> t a -> b termSummary :: Term (Patch a) Info -> DiffSummary termSummary = cata summary where From a03be353d60d482787b043ae97afb3c26422bf6a Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Apr 2016 22:14:53 -0400 Subject: [PATCH 03/53] Expose Patch constructor and add summary --- src/DiffSummary.hs | 38 ++++++++++++++++++++++++-------------- src/Patch.hs | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 5428dbe77..93065eb10 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -6,32 +6,42 @@ import Info import Patch import Term import Syntax +import qualified Data.Text as T +import qualified Category import Data.Monoid +import Data.Maybe (listToMaybe) +import Data.Set (toList) +import Control.Arrow +import Control.Monad -newtype DiffSummary = DiffSummary { diffChanges :: [DiffChange] } + +newtype DiffSummary = DiffSummary { diffChanges :: [Patch DiffEntry] } deriving (Monoid) emptyDiffSummary :: DiffSummary emptyDiffSummary = DiffSummary { diffChanges = [] } -data DiffChange = Insertion { category :: String, termName :: String } - +newtype DiffEntry = DiffEntry { termName :: String } patchSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary -patchSummary termSummary patch = maybe emptyDiffSummary termSummary beforeTerm <> - maybe emptyDiffSummary termSummary afterTerm +patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (after patch) where - beforeTerm = before patch - afterTerm = after patch + memptyOrDiff = maybe emptyDiffSummary termSummary diffSummary :: Diff (Patch a) Info -> DiffSummary diffSummary = foldMap (patchSummary termSummary) -termSummary :: Term (Patch a) Info -> DiffSummary +-- Syntax Text DiffSummary -> DiffSummary Text + +termSummary :: Term T.Text Info -> T.Text termSummary = cata summary where - summary _ (Leaf (Replace _ _)) = emptyDiffSummary - summary _ (Leaf (Insert _)) = emptyDiffSummary - summary _ (Leaf (Delete _)) = emptyDiffSummary - summary _ (Indexed _) = emptyDiffSummary - summary _ (Fixed _) = emptyDiffSummary - summary _ (Keyed _) = emptyDiffSummary + summary :: Info -> Syntax T.Text f -> T.Text + summary info (Leaf replace) = replace + summary info (Indexed children) = toCategory info + summary info (Fixed children) = toCategory info + summary info (Keyed _) = toCategory info + + toCategory :: Info -> T.Text + toCategory term = T.pack $ case listToMaybe . toList $ Category.categories term of + Just category -> show category + Nothing -> "Unknown" diff --git a/src/Patch.hs b/src/Patch.hs index 8eb3bd888..0e96df2f2 100644 --- a/src/Patch.hs +++ b/src/Patch.hs @@ -1,4 +1,4 @@ -module Patch where +module Patch (Patch(..), after, before, unPatch, patchSum) where import Data.Bifunctor.These From 658fd024ecf75f9a8901e4bbfa2dd97a8a10aabb Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 26 Apr 2016 17:31:05 -0400 Subject: [PATCH 04/53] Add recursion-schemes and free --- semantic-diff.cabal | 2 ++ 1 file changed, 2 insertions(+) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index cf3afb1df..acb346603 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -61,6 +61,8 @@ library , text-icu , tree-sitter-parsers , vector + , recursion-schemes + , free default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, OverloadedStrings ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j From 6c07d4dc55dcae6e8c8639b7b473632a7ab8e261 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 12:17:38 -0400 Subject: [PATCH 05/53] WIP CofreeF --- semantic-diff.cabal | 2 +- src/Alignment.hs | 28 +++++++++++++++++----------- src/Category.hs | 5 +++-- src/Control/Comonad/Cofree.hs | 22 ---------------------- src/Control/Comonad/CofreeExtras.hs | 11 +++++++++++ src/DiffSummary.hs | 24 ++++++++++++++++-------- src/Diffing.hs | 3 ++- src/Interpreter.hs | 18 +++++++----------- src/Parser.hs | 11 ++++++----- src/Renderer/JSON.hs | 2 +- src/Renderer/Patch.hs | 2 +- src/Renderer/Split.hs | 3 ++- src/Term.hs | 21 ++++++++++++++------- 13 files changed, 81 insertions(+), 71 deletions(-) delete mode 100644 src/Control/Comonad/Cofree.hs create mode 100644 src/Control/Comonad/CofreeExtras.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index acb346603..972c37ccf 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -16,7 +16,7 @@ library exposed-modules: Algorithm , Alignment , Category - , Control.Comonad.Cofree + , Control.Comonad.CofreeExtras , Control.Monad.Free , Data.Adjoined , Data.Align diff --git a/src/Alignment.hs b/src/Alignment.hs index 35f820f6e..d62bcf8a5 100644 --- a/src/Alignment.hs +++ b/src/Alignment.hs @@ -9,7 +9,8 @@ module Alignment ) where import Control.Arrow -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable hiding (Foldable) import Control.Monad import Control.Monad.Free import Data.Adjoined @@ -48,22 +49,27 @@ hasChanges = or . fmap (or . (True <$)) -- | Split a diff, which may span multiple lines, into rows of split diffs paired with the Range of characters spanned by that Row on each side of the diff. splitDiffByLines :: Both (Source Char) -> Diff leaf Info -> [Row (SplitDiff leaf Info, Range)] -splitDiffByLines sources = toList . iter (\ (Annotated infos syntax) -> splitAbstractedTerm ((Free .) . Annotated) sources infos syntax) . fmap (splitPatchByLines sources) +splitDiffByLines sources = toList . iter (\ (Annotated infos syntax) -> splitAbstractedTerm ((Free .) . Annotated) sources (infos :< syntax)) . fmap (splitPatchByLines sources) -- | Split a patch, which may span multiple lines, into rows of split diffs. splitPatchByLines :: Both (Source Char) -> Patch (Term leaf Info) -> Adjoined (Both (Line (SplitDiff leaf Info, Range))) splitPatchByLines sources patch = wrapTermInPatch <$> splitAndFoldTerm (unPatch patch) - where splitAndFoldTerm (This deleted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm (:<) (Identity $ fst sources)) (Identity <$> deleted)) nil - splitAndFoldTerm (That inserted) = tsequenceL mempty $ both nil (runIdentity <$> cata (splitAbstractedTerm (:<) (Identity $ snd sources)) (Identity <$> inserted)) - splitAndFoldTerm (These deleted inserted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm (:<) (Identity $ fst sources)) (Identity <$> deleted)) (runIdentity <$> cata (splitAbstractedTerm (:<) (Identity $ snd sources)) (Identity <$> inserted)) - wrapTermInPatch = fmap (fmap (first (Pure . constructor patch))) - constructor (Replace _ _) = SplitReplace - constructor (Insert _) = SplitInsert - constructor (Delete _) = SplitDelete + where + splitAndFoldTerm :: These (Term leaf Info) (Term leaf Info) -> Adjoined (Both (Line (Term leaf Info, Range))) + splitAndFoldTerm (This deleted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ fst sources)) (hylo (Fix . annotationMap Identity) unfix deleted)) nil + splitAndFoldTerm (That inserted) = tsequenceL mempty $ both nil (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ snd sources)) (hylo (Fix . annotationMap Identity) unfix inserted)) + splitAndFoldTerm (These deleted inserted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ fst sources)) (hylo (Fix . annotationMap Identity) unfix deleted)) (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ snd sources)) (hylo (Fix . annotationMap Identity) unfix inserted)) + wrapTermInPatch = fmap (fmap (first (Pure . constructor patch))) + constructor (Replace _ _) = SplitReplace + constructor (Insert _) = SplitInsert + constructor (Delete _) = SplitDelete + +annotationMap :: (a -> b) -> TermF leaf a f -> TermF leaf b f +annotationMap f (a :< r) = f a :< r -- | Split a term comprised of an Info & Syntax up into one `outTerm` (abstracted by an alignment function & constructor) per line in `Source`. -splitAbstractedTerm :: (Applicative f, Coalescent (f (Line (Maybe (Identity outTerm), Range))), Coalescent (f (Line (Maybe (T.Text, outTerm), Range))), Foldable f, TotalCrosswalk f) => (Info -> Syntax leaf outTerm -> outTerm) -> f (Source Char) -> f Info -> Syntax leaf (Adjoined (f (Line (outTerm, Range)))) -> Adjoined (f (Line (outTerm, Range))) -splitAbstractedTerm makeTerm sources infos syntax = case syntax of +splitAbstractedTerm :: (Applicative f, Coalescent (f (Line (Maybe (Identity outTerm), Range))), Coalescent (f (Line (Maybe (T.Text, outTerm), Range))), Foldable f, TotalCrosswalk f) => (Info -> Syntax leaf outTerm -> outTerm) -> f (Source Char) -> CofreeF (Syntax leaf) (f Info) (Adjoined (f (Line (outTerm, Range)))) -> Adjoined (f (Line (outTerm, Range))) +splitAbstractedTerm makeTerm sources (infos :< syntax) = case syntax of Leaf a -> let lineRanges = linesInRangeOfSource <$> (characterRange <$> infos) <*> sources in tsequenceL (pure mempty) $ fmap <$> ((\ info -> fmap (\ range -> (makeTerm info { characterRange = range } (Leaf a), range))) <$> infos) <*> lineRanges diff --git a/src/Category.hs b/src/Category.hs index d60f68556..a975f3e48 100644 --- a/src/Category.hs +++ b/src/Category.hs @@ -2,7 +2,8 @@ module Category where import Term -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import Data.Set -- | A standardized category of AST node. Used to determine the semantics for @@ -33,7 +34,7 @@ class Categorizable a where categories :: a -> Set Category instance Categorizable annotation => Categorizable (Term a annotation) where - categories (annotation :< _) = categories annotation + categories (Fix (annotation :< _)) = categories annotation -- | Test whether the categories from the categorizables intersect. comparable :: Categorizable a => a -> a -> Bool diff --git a/src/Control/Comonad/Cofree.hs b/src/Control/Comonad/Cofree.hs deleted file mode 100644 index dfb3b7170..000000000 --- a/src/Control/Comonad/Cofree.hs +++ /dev/null @@ -1,22 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -module Control.Comonad.Cofree where - -import Data.Copointed - -data Cofree functor annotation = annotation :< (functor (Cofree functor annotation)) - deriving (Functor, Foldable, Traversable) - -instance (Eq annotation, Eq (functor (Cofree functor annotation))) => Eq (Cofree functor annotation) where - a :< f == b :< g = a == b && f == g - -instance (Show annotation, Show (functor (Cofree functor annotation))) => Show (Cofree functor annotation) where - showsPrec n (a :< f) = showsPrec n a . (" :< " ++) . showsPrec n f - -unwrap :: Cofree functor annotation -> functor (Cofree functor annotation) -unwrap (_ :< f) = f - -unfold :: Functor functor => (seed -> (annotation, functor seed)) -> seed -> Cofree functor annotation -unfold grow seed = case grow seed of (annotation, functor) -> annotation :< (unfold grow <$> functor) - -instance Copointed (Cofree functor) where - copoint (annotation :< _) = annotation diff --git a/src/Control/Comonad/CofreeExtras.hs b/src/Control/Comonad/CofreeExtras.hs new file mode 100644 index 000000000..5121e4237 --- /dev/null +++ b/src/Control/Comonad/CofreeExtras.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE UndecidableInstances #-} +module Control.Comonad.CofreeExtras where + +import Control.Comonad.Cofree +import Data.Copointed + +unwrap :: Cofree functor annotation -> functor (Cofree functor annotation) +unwrap (_ :< f) = f + +instance Copointed (Cofree functor) where + copoint (annotation :< _) = annotation diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 93065eb10..78adb00c5 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -13,6 +13,8 @@ import Data.Maybe (listToMaybe) import Data.Set (toList) import Control.Arrow import Control.Monad +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable as Foldable newtype DiffSummary = DiffSummary { diffChanges :: [Patch DiffEntry] } @@ -29,19 +31,25 @@ patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (af memptyOrDiff = maybe emptyDiffSummary termSummary diffSummary :: Diff (Patch a) Info -> DiffSummary -diffSummary = foldMap (patchSummary termSummary) +diffSummary = histo diffSummary' where + diffSummary' :: TermF (Diff (Patch a) Info) (Cofree (TermF a Info) DiffSummary) f -> DiffSummary + diffSummary' (info :< Leaf replace) = _ + -- (patchSummary termSummary) -- Syntax Text DiffSummary -> DiffSummary Text +-- If termSummary returns a DiffEntry that just contains the term name, we need to +-- Instead of foldMap we need a histomorphism termSummary :: Term T.Text Info -> T.Text -termSummary = cata summary where - summary :: Info -> Syntax T.Text f -> T.Text - summary info (Leaf replace) = replace - summary info (Indexed children) = toCategory info - summary info (Fixed children) = toCategory info - summary info (Keyed _) = toCategory info +termSummary = Foldable.cata summary where + summary :: TermF T.Text Info f -> T.Text + summary (info :< Leaf replace) = replace + summary (info :< Indexed children) = toCategory info + summary (info :< Fixed children) = toCategory info + summary (info :< Keyed _) = toCategory info toCategory :: Info -> T.Text - toCategory term = T.pack $ case listToMaybe . toList $ Category.categories term of + toCategory term = T.pack $ case maybeFirstCategory term of Just category -> show category Nothing -> "Unknown" + maybeFirstCategory term = listToMaybe . toList $ Category.categories term diff --git a/src/Diffing.hs b/src/Diffing.hs index 995f57396..e1157e373 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -14,9 +14,10 @@ import TreeSitter import Text.Parser.TreeSitter.Language import Control.Monad.Free -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree import Data.Copointed import Data.Functor.Both +import Data.Functor.Foldable import qualified Data.ByteString.Char8 as B1 import Data.Foldable import Data.Monoid diff --git a/src/Interpreter.hs b/src/Interpreter.hs index c35cb4df1..1a4feef44 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -3,7 +3,8 @@ module Interpreter (interpret, Comparable, diffTerms) where import Algorithm import Category import Control.Arrow -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import Control.Monad.Free import Data.Copointed import Data.Functor.Both @@ -31,30 +32,25 @@ diffTerms cost = interpret comparable cost interpret :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Diff a annotation interpret comparable cost a b = fromMaybe (Pure $ Replace a b) $ constructAndRun comparable cost a b --- | A hylomorphism. Given an `a`, unfold and then refold into a `b`. -hylo :: Functor f => (t -> f b -> b) -> (a -> (t, f a)) -> a -> b -hylo down up a = down annotation $ hylo down up <$> syntax where - (annotation, syntax) = up a - -- | Constructs an algorithm and runs it constructAndRun :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Maybe (Diff a annotation) -constructAndRun _ _ a b | a == b = hylo (curry $ Free . uncurry Annotated) (copoint &&& unwrap) <$> zipTerms a b where +constructAndRun _ _ a b | a == b = hylo (\termF -> Free $ Annotated (headF termF) (tailF termF)) unfix <$> zipTerms a b constructAndRun comparable _ a b | not $ comparable a b = Nothing -constructAndRun comparable cost (annotation1 :< a) (annotation2 :< b) = +constructAndRun comparable cost (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) = run comparable cost $ algorithm a b where algorithm (Indexed a') (Indexed b') = Free $ ByIndex a' b' (annotate . Indexed) algorithm (Keyed a') (Keyed b') = Free $ ByKey a' b' (annotate . Keyed) algorithm (Leaf a') (Leaf b') | a' == b' = annotate $ Leaf b' - algorithm a' b' = Free $ Recursive (annotation1 :< a') (annotation2 :< b') Pure + algorithm a' b' = Free $ Recursive (Fix (annotation1 :< a')) (Fix (annotation2 :< b')) Pure annotate = Pure . Free . Annotated (Both (annotation1, annotation2)) -- | Runs the diff algorithm run :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation) run _ _ (Pure diff) = Just diff -run comparable cost (Free (Recursive (annotation1 :< a) (annotation2 :< b) f)) = run comparable cost . f $ recur a b where +run comparable cost (Free (Recursive (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) f)) = run comparable cost . f $ recur a b where recur (Indexed a') (Indexed b') | length a' == length b' = annotate . Indexed $ zipWith (interpret comparable cost) a' b' recur (Fixed a') (Fixed b') | length a' == length b' = annotate . Fixed $ zipWith (interpret comparable cost) a' b' recur (Keyed a') (Keyed b') | Map.keys a' == bKeys = annotate . Keyed . Map.fromList . fmap repack $ bKeys @@ -62,7 +58,7 @@ run comparable cost (Free (Recursive (annotation1 :< a) (annotation2 :< b) f)) = bKeys = Map.keys b' repack key = (key, interpretInBoth key a' b') interpretInBoth key x y = interpret comparable cost (x ! key) (y ! key) - recur _ _ = Pure $ Replace (annotation1 :< a) (annotation2 :< b) + recur _ _ = Pure $ Replace (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) annotate = Free . Annotated (Both (annotation1, annotation2)) diff --git a/src/Parser.hs b/src/Parser.hs index e0847b6a4..6220b6dce 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -5,8 +5,8 @@ import Info import Range import Syntax import Term -import Control.Comonad.Cofree -import Data.Copointed +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import qualified Data.OrderedMap as Map import qualified Data.Set as Set import Source @@ -40,13 +40,14 @@ isFixed = not . Set.null . Set.intersection fixedCategories -- | Given a function that maps production names to sets of categories, produce -- | a Constructor. termConstructor :: (String -> Set.Set Category) -> Constructor -termConstructor mapping source range name children = Info range categories (1 + sum (size . copoint <$> children)) :< construct children +termConstructor mapping source range name children = Fix (Info range categories (1 + sum (size . headF . unfix <$> children)) :< construct children) where categories = mapping name + construct :: [Term Text Info] -> Syntax Text (Term Text Info) construct [] = Leaf . pack . toString $ slice range source construct children | isFixed categories = Fixed children construct children | isKeyed categories = Keyed . Map.fromList $ assignKey <$> children construct children = Indexed children - assignKey node@(Info _ categories _ :< Fixed (key : _)) | Set.member Pair categories = (getSubstring key, node) + assignKey node@(Fix (Info _ categories _ :< Fixed (key : _))) | Set.member Pair categories = (getSubstring key, node) assignKey node = (getSubstring node, node) - getSubstring (Info range _ _ :< _) = pack . toString $ slice range source + getSubstring (Fix (Info range _ _ :< _)) = pack . toString $ slice range source diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index 1c06c4ca0..e70aec66f 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -6,7 +6,7 @@ module Renderer.JSON ( import Alignment import Category -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree import Control.Monad.Free import Data.Aeson hiding (json) import Data.Aeson.Encode diff --git a/src/Renderer/Patch.hs b/src/Renderer/Patch.hs index b6f3b1bff..6930a3693 100644 --- a/src/Renderer/Patch.hs +++ b/src/Renderer/Patch.hs @@ -15,7 +15,7 @@ import Range import Renderer import Source hiding ((++), break) import SplitDiff -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree import Control.Monad.Free import Data.Functor.Both as Both import Data.List diff --git a/src/Renderer/Split.hs b/src/Renderer/Split.hs index d190ba242..37c4fd370 100644 --- a/src/Renderer/Split.hs +++ b/src/Renderer/Split.hs @@ -3,10 +3,11 @@ module Renderer.Split where import Alignment import Category -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree import Control.Monad.Free import Data.Foldable import Data.Functor.Both +import Data.Functor.Foldable import Data.Monoid import qualified Data.Text.Lazy as TL import Diff diff --git a/src/Term.hs b/src/Term.hs index 92be3f1d5..bd2eb9444 100644 --- a/src/Term.hs +++ b/src/Term.hs @@ -1,20 +1,27 @@ +{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-} module Term where -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.Maybe import Data.OrderedMap hiding (size) import Syntax +unfix :: Fix f -> f (Fix f) +unfix (Fix f) = f + -- | An annotated node (Syntax) in an abstract syntax tree. -type Term a annotation = Cofree (Syntax a) annotation +type TermF a annotation = CofreeF (Syntax a) annotation +type Term a annotation = Fix (TermF a annotation) +type instance Base (Term a annotation) = (TermF a annotation) -- | Zip two terms by combining their annotations into a pair of annotations. -- | If the structure of the two terms don't match, then Nothing will be returned. zipTerms :: Term a annotation -> Term a annotation -> Maybe (Term a (Both annotation)) -zipTerms (annotation1 :< a) (annotation2 :< b) = annotate $ zipUnwrap a b +zipTerms (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) = annotate $ zipUnwrap a b where - annotate = fmap (Both (annotation1, annotation2) :<) + annotate = fmap (Fix . (Both (annotation1, annotation2) :<)) zipUnwrap (Leaf _) (Leaf b') = Just $ Leaf b' zipUnwrap (Indexed a') (Indexed b') = Just . Indexed . catMaybes $ zipWith zipTerms a' b' zipUnwrap (Fixed a') (Fixed b') = Just . Fixed . catMaybes $ zipWith zipTerms a' b' @@ -23,10 +30,10 @@ zipTerms (annotation1 :< a) (annotation2 :< b) = annotate $ zipUnwrap a b zipUnwrapMaps a' b' key = (,) key <$> zipTerms (a' ! key) (b' ! key) -- | Fold a term into some other value, starting with the leaves. -cata :: (annotation -> Syntax a b -> b) -> Term a annotation -> b -cata f (annotation :< syntax) = f annotation $ cata f <$> syntax +-- cata :: (annotation -> Syntax a b -> b) -> Term a annotation -> b +-- cata f (annotation :< syntax) = f annotation $ cata f <$> syntax -- | Return the node count of a term. termSize :: Term a annotation -> Integer termSize = cata size where - size _ syntax = 1 + sum syntax + size (_ :< syntax) = 1 + sum syntax From 4eedcc4a70f6b8001a0fc8ce413b1350703c1f8f Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 12:36:59 -0400 Subject: [PATCH 06/53] Fix up other catas --- src/Diffing.hs | 20 ++++++++++---------- src/Renderer/JSON.hs | 9 +++++---- src/Renderer/Patch.hs | 3 ++- src/Renderer/Split.hs | 6 +++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Diffing.hs b/src/Diffing.hs index e1157e373..bae93bcdd 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -15,7 +15,6 @@ import Text.Parser.TreeSitter.Language import Control.Monad.Free import Control.Comonad.Trans.Cofree -import Data.Copointed import Data.Functor.Both import Data.Functor.Foldable import qualified Data.ByteString.Char8 as B1 @@ -36,8 +35,8 @@ parserForType mediaType = case languageForType mediaType of -- | A fallback parser that treats a file simply as rows of strings. lineByLineParser :: Parser -lineByLineParser input = return . root $ case foldl' annotateLeaves ([], 0) lines of - (leaves, _) -> leaves +lineByLineParser input = return . Fix . root $ case foldl' annotateLeaves ([], 0) lines of + (leaves, _) -> Fix <$> leaves where lines = actualLines input root children = Info (Range 0 $ length input) mempty (1 + fromIntegral (length children)) :< Indexed children @@ -55,14 +54,15 @@ parserForFilepath = parserForType . T.pack . takeExtension breakDownLeavesByWord :: Source Char -> Term T.Text Info -> Term T.Text Info breakDownLeavesByWord source = cata replaceIn where - replaceIn (Info range categories _) (Leaf _) + replaceIn :: TermF T.Text Info (Term T.Text Info) -> Term T.Text Info + replaceIn (Info range categories _ :< Leaf _) | ranges <- rangesAndWordsInSource range , length ranges > 1 - = Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges) - replaceIn info@(Info range categories _) syntax - = Info range categories (1 + sum (size . copoint <$> syntax)) :< syntax + = Fix $ Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges) + replaceIn (Info range categories _ :< syntax) + = Fix $ Info range categories (1 + sum (size . headF . unfix <$> syntax)) :< syntax rangesAndWordsInSource range = rangesAndWordsFrom (start range) (toString $ slice range source) - makeLeaf categories (range, substring) = Info range categories 1 :< Leaf (T.pack substring) + makeLeaf categories (range, substring) = Fix $ Info range categories 1 :< Leaf (T.pack substring) -- | Transcode a file to a unicode source. transcode :: B1.ByteString -> IO (Source Char) @@ -90,9 +90,9 @@ diffFiles parser renderer sourceBlobs = do -- | The sum of the node count of the diff’s patches. diffCostWithCachedTermSizes :: Diff a Info -> Integer -diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . copoint)) +diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . headF . unfix)) -- | The absolute difference between the node counts of a diff. diffCostWithAbsoluteDifferenceOfCachedDiffSizes :: Diff a Info -> Integer diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Free (Annotated (Both (before, after)) _)) = abs $ size before - size after -diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Pure patch) = sum $ size . copoint <$> patch +diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Pure patch) = sum $ size . headF . unfix <$> patch diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index e70aec66f..d95d2a19d 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -7,6 +7,7 @@ module Renderer.JSON ( import Alignment import Category import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import Control.Monad.Free import Data.Aeson hiding (json) import Data.Aeson.Encode @@ -54,8 +55,8 @@ instance ToJSON value => ToJSON (OrderedMap T.Text value) where toJSON map = object $ uncurry (.=) <$> toList map toEncoding map = pairs . mconcat $ uncurry (.=) <$> toList map instance ToJSON (Term leaf Info) where - toJSON (info :< syntax) = object (termFields info syntax) - toEncoding (info :< syntax) = pairs $ mconcat (termFields info syntax) + toJSON (Fix (info :< syntax)) = object (termFields info syntax) + toEncoding (Fix (info :< syntax)) = pairs $ mconcat (termFields info syntax) lineFields :: KeyValue kv => Int -> Line (SplitDiff leaf Info, Range) -> [kv] lineFields n line | isEmpty line = [] @@ -73,9 +74,9 @@ termFields (Info range categories _) syntax = "range" .= range : "categories" .= Keyed c -> childrenFields c where childrenFields c = [ "children" .= c ] -patchFields :: KeyValue kv => SplitPatch (Cofree (Syntax leaf) Info) -> [kv] +patchFields :: KeyValue kv => SplitPatch (Term leaf Info) -> [kv] patchFields patch = case patch of SplitInsert term -> fields "insert" term SplitDelete term -> fields "delete" term SplitReplace term -> fields "replace" term - where fields kind (info :< syntax) = "patch" .= T.pack kind : termFields info syntax + where fields kind (Fix (info :< syntax)) = "patch" .= T.pack kind : termFields info syntax diff --git a/src/Renderer/Patch.hs b/src/Renderer/Patch.hs index 6930a3693..58b9acc8c 100644 --- a/src/Renderer/Patch.hs +++ b/src/Renderer/Patch.hs @@ -16,6 +16,7 @@ import Renderer import Source hiding ((++), break) import SplitDiff import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import Control.Monad.Free import Data.Functor.Both as Both import Data.List @@ -86,7 +87,7 @@ showLine source line | isEmpty line = Nothing -- | Return the range from a split diff. getRange :: SplitDiff leaf Info -> Range getRange (Free (Annotated (Info range _ _) _)) = range -getRange (Pure patch) = let Info range _ _ :< _ = getSplitTerm patch in range +getRange (Pure patch) = let (Fix (Info range _ _ :< _)) = getSplitTerm patch in range -- | Returns the header given two source blobs and a hunk. header :: Both SourceBlob -> String diff --git a/src/Renderer/Split.hs b/src/Renderer/Split.hs index 37c4fd370..c3ebc47b9 100644 --- a/src/Renderer/Split.hs +++ b/src/Renderer/Split.hs @@ -29,7 +29,7 @@ import qualified Text.Blaze.Internal as Blaze -- | Add the first category from a Foldable of categories as a class name as a -- | class name on the markup, prefixed by `category-`. -classifyMarkup :: Foldable f => f Category -> Markup -> Markup +classifyMarkup :: Prelude.Foldable f => f Category -> Markup -> Markup classifyMarkup categories element = maybe element ((element !) . A.class_ . stringValue . styleName) $ maybeFirst categories -- | Return the appropriate style name for the given category. @@ -104,12 +104,12 @@ instance ToMarkup f => ToMarkup (Renderable (Source Char, Info, Syntax a (f, Ran elements ++ [ string . toString $ slice (Range previous $ end range) source ] instance ToMarkup (Renderable (Source Char, Term a Info)) where - toMarkup (Renderable (source, term)) = Prelude.fst $ cata (\ info@(Info range _ _) syntax -> (toMarkup $ Renderable (source, info, syntax), range)) term + toMarkup (Renderable (source, term)) = Prelude.fst $ cata (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) term instance ToMarkup (Renderable (Source Char, SplitDiff a Info)) where toMarkup (Renderable (source, diff)) = Prelude.fst $ iter (\ (Annotated info@(Info range _ _) syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) $ toMarkupAndRange <$> diff where toMarkupAndRange :: SplitPatch (Term a Info) -> (Markup, Range) - toMarkupAndRange patch = let term@(Info range _ _ :< _) = getSplitTerm patch in + toMarkupAndRange patch = let term@(Fix (Info range _ _ :< _)) = getSplitTerm patch in ((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue . show $ termSize term)) . toMarkup $ Renderable (source, term), range) From a789af8f31c8abd1b22dca03d05993a9ad0dc24c Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 14:10:25 -0400 Subject: [PATCH 07/53] Define Annotated in terms of CofreeF --- src/Diff.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Diff.hs b/src/Diff.hs index fc35f26a2..ca23c362f 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -1,5 +1,6 @@ module Diff where +import Control.Comonad.Trans.Cofree import Control.Monad.Free import Data.Functor.Both import Patch @@ -7,11 +8,16 @@ import Syntax import Term -- | An annotated syntax in a diff tree. -data Annotated a annotation f = Annotated { annotation :: !annotation, syntax :: !(Syntax a f) } - deriving (Functor, Eq, Show, Foldable) +type Annotated a annotation f = CofreeF (Syntax a) annotation f + +annotation :: Annotated a annotation f -> annotation +annotation = headF + +syntax :: Annotated a annotation f -> Syntax a f +syntax = tailF -- | An annotated series of patches of terms. -type Diff a annotation = Free (Annotated a (Both annotation)) (Patch (Term a annotation)) +type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) -- | Sum the result of a transform applied to all the patches in the diff. diffSum :: (Patch (Term a annotation) -> Integer) -> Diff a annotation -> Integer From fc0e9236e92ef10d065cc95746eeb9514970a286 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 14:10:36 -0400 Subject: [PATCH 08/53] Stub diffSummary' --- src/DiffSummary.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 78adb00c5..37b7b4dc7 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -30,10 +30,11 @@ patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (af where memptyOrDiff = maybe emptyDiffSummary termSummary -diffSummary :: Diff (Patch a) Info -> DiffSummary +diffSummary :: Diff a Info -> DiffSummary diffSummary = histo diffSummary' where - diffSummary' :: TermF (Diff (Patch a) Info) (Cofree (TermF a Info) DiffSummary) f -> DiffSummary - diffSummary' (info :< Leaf replace) = _ + diffSummary' :: DiffF a (Cofree (DiffF a Info) DiffSummary) f -> DiffSummary + diffSummary' (coDiffSummary :< Leaf _) = diffSummary + where (diffSummary :< _) = runCofree coDiffSummary -- (patchSummary termSummary) -- Syntax Text DiffSummary -> DiffSummary Text From 9f8fc439b3fcfcf31a61be9d7cfc151aaa31ca16 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 14:19:10 -0400 Subject: [PATCH 09/53] typealias Annotated to CofreeF --- src/Alignment.hs | 2 +- src/Diff.hs | 3 +++ src/Diffing.hs | 2 +- src/Interpreter.hs | 8 +++----- src/Renderer/JSON.hs | 4 ++-- src/Renderer/Patch.hs | 2 +- src/Renderer/Split.hs | 3 +-- src/SplitDiff.hs | 5 +++-- 8 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Alignment.hs b/src/Alignment.hs index d62bcf8a5..73c17d0a0 100644 --- a/src/Alignment.hs +++ b/src/Alignment.hs @@ -49,7 +49,7 @@ hasChanges = or . fmap (or . (True <$)) -- | Split a diff, which may span multiple lines, into rows of split diffs paired with the Range of characters spanned by that Row on each side of the diff. splitDiffByLines :: Both (Source Char) -> Diff leaf Info -> [Row (SplitDiff leaf Info, Range)] -splitDiffByLines sources = toList . iter (\ (Annotated infos syntax) -> splitAbstractedTerm ((Free .) . Annotated) sources (infos :< syntax)) . fmap (splitPatchByLines sources) +splitDiffByLines sources = toList . iter (\ (infos :< syntax) -> splitAbstractedTerm ((Free .) . (:<)) sources (infos :< syntax)) . fmap (splitPatchByLines sources) -- | Split a patch, which may span multiple lines, into rows of split diffs. splitPatchByLines :: Both (Source Char) -> Patch (Term leaf Info) -> Adjoined (Both (Line (SplitDiff leaf Info, Range))) diff --git a/src/Diff.hs b/src/Diff.hs index ca23c362f..dca069b7d 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -16,6 +16,9 @@ annotation = headF syntax :: Annotated a annotation f -> Syntax a f syntax = tailF +annotate :: annotation -> Syntax a f -> CofreeF (Syntax a) annotation f +annotate = (:<) + -- | An annotated series of patches of terms. type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) diff --git a/src/Diffing.hs b/src/Diffing.hs index bae93bcdd..dc3a38b38 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -94,5 +94,5 @@ diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . headF . un -- | The absolute difference between the node counts of a diff. diffCostWithAbsoluteDifferenceOfCachedDiffSizes :: Diff a Info -> Integer -diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Free (Annotated (Both (before, after)) _)) = abs $ size before - size after +diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Free (Both (before, after) :< _)) = abs $ size before - size after diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Pure patch) = sum $ size . headF . unfix <$> patch diff --git a/src/Interpreter.hs b/src/Interpreter.hs index 1a4feef44..479b38464 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -2,11 +2,9 @@ module Interpreter (interpret, Comparable, diffTerms) where import Algorithm import Category -import Control.Arrow import Control.Comonad.Trans.Cofree import Data.Functor.Foldable import Control.Monad.Free -import Data.Copointed import Data.Functor.Both import qualified Data.OrderedMap as Map import qualified Data.List as List @@ -34,7 +32,7 @@ interpret comparable cost a b = fromMaybe (Pure $ Replace a b) $ constructAndRun -- | Constructs an algorithm and runs it constructAndRun :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Maybe (Diff a annotation) -constructAndRun _ _ a b | a == b = hylo (\termF -> Free $ Annotated (headF termF) (tailF termF)) unfix <$> zipTerms a b +constructAndRun _ _ a b | a == b = hylo (\termF -> Free $ headF termF :< tailF termF) unfix <$> zipTerms a b constructAndRun comparable _ a b | not $ comparable a b = Nothing @@ -44,7 +42,7 @@ constructAndRun comparable cost (Fix (annotation1 :< a)) (Fix (annotation2 :< b) algorithm (Keyed a') (Keyed b') = Free $ ByKey a' b' (annotate . Keyed) algorithm (Leaf a') (Leaf b') | a' == b' = annotate $ Leaf b' algorithm a' b' = Free $ Recursive (Fix (annotation1 :< a')) (Fix (annotation2 :< b')) Pure - annotate = Pure . Free . Annotated (Both (annotation1, annotation2)) + annotate = Pure . Free . (Both (annotation1, annotation2) :<) -- | Runs the diff algorithm run :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation) @@ -60,7 +58,7 @@ run comparable cost (Free (Recursive (Fix (annotation1 :< a)) (Fix (annotation2 interpretInBoth key x y = interpret comparable cost (x ! key) (y ! key) recur _ _ = Pure $ Replace (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) - annotate = Free . Annotated (Both (annotation1, annotation2)) + annotate = Free . (Both (annotation1, annotation2) :<) run comparable cost (Free (ByKey a b f)) = run comparable cost $ f byKey where byKey = Map.fromList $ toKeyValue <$> List.union aKeys bKeys diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index d95d2a19d..6dbed185d 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -47,9 +47,9 @@ instance ToJSON a => ToJSON (Both a) where toJSON (Both (a, b)) = Array . fromList $ toJSON <$> [ a, b ] toEncoding = foldable instance ToJSON (SplitDiff leaf Info) where - toJSON (Free (Annotated info syntax)) = object (termFields info syntax) + toJSON (Free (info :< syntax)) = object (termFields info syntax) toJSON (Pure patch) = object (patchFields patch) - toEncoding (Free (Annotated info syntax)) = pairs $ mconcat (termFields info syntax) + toEncoding (Free (info :< syntax)) = pairs $ mconcat (termFields info syntax) toEncoding (Pure patch) = pairs $ mconcat (patchFields patch) instance ToJSON value => ToJSON (OrderedMap T.Text value) where toJSON map = object $ uncurry (.=) <$> toList map diff --git a/src/Renderer/Patch.hs b/src/Renderer/Patch.hs index 58b9acc8c..742694997 100644 --- a/src/Renderer/Patch.hs +++ b/src/Renderer/Patch.hs @@ -86,7 +86,7 @@ showLine source line | isEmpty line = Nothing -- | Return the range from a split diff. getRange :: SplitDiff leaf Info -> Range -getRange (Free (Annotated (Info range _ _) _)) = range +getRange (Free (Info range _ _ :< _)) = range getRange (Pure patch) = let (Fix (Info range _ _ :< _)) = getSplitTerm patch in range -- | Returns the header given two source blobs and a hunk. diff --git a/src/Renderer/Split.hs b/src/Renderer/Split.hs index c3ebc47b9..2b08225db 100644 --- a/src/Renderer/Split.hs +++ b/src/Renderer/Split.hs @@ -10,7 +10,6 @@ import Data.Functor.Both import Data.Functor.Foldable import Data.Monoid import qualified Data.Text.Lazy as TL -import Diff import Info import Line import Prelude hiding (div, head, span, fst, snd) @@ -107,7 +106,7 @@ instance ToMarkup (Renderable (Source Char, Term a Info)) where toMarkup (Renderable (source, term)) = Prelude.fst $ cata (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) term instance ToMarkup (Renderable (Source Char, SplitDiff a Info)) where - toMarkup (Renderable (source, diff)) = Prelude.fst $ iter (\ (Annotated info@(Info range _ _) syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) $ toMarkupAndRange <$> diff + toMarkup (Renderable (source, diff)) = Prelude.fst $ iter (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) $ toMarkupAndRange <$> diff where toMarkupAndRange :: SplitPatch (Term a Info) -> (Markup, Range) toMarkupAndRange patch = let term@(Fix (Info range _ _ :< _)) = getSplitTerm patch in ((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue . show $ termSize term)) . toMarkup $ Renderable (source, term), range) diff --git a/src/SplitDiff.hs b/src/SplitDiff.hs index 5b4ea0a94..87d5f3fb8 100644 --- a/src/SplitDiff.hs +++ b/src/SplitDiff.hs @@ -1,7 +1,8 @@ module SplitDiff where -import Diff (Annotated) +import Syntax import Control.Monad.Free (Free) +import Control.Comonad.Trans.Cofree import Term (Term) -- | A patch to only one side of a diff. @@ -15,4 +16,4 @@ getSplitTerm (SplitDelete a) = a getSplitTerm (SplitReplace a) = a -- | A diff with only one side’s annotations. -type SplitDiff leaf annotation = Free (Annotated leaf annotation) (SplitPatch (Term leaf annotation)) +type SplitDiff leaf annotation = Free (CofreeF (Syntax leaf) annotation) (SplitPatch (Term leaf annotation)) From 1914950bc106d9e8740f818eb73d7e368756a2fc Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 3 May 2016 15:50:38 -0400 Subject: [PATCH 10/53] Stub diffSummary' --- semantic-diff.cabal | 1 + src/Diff.hs | 10 ++++++++-- src/DiffSummary.hs | 22 +++++++++++++++++----- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index 972c37ccf..65baf4ebd 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -63,6 +63,7 @@ library , vector , recursion-schemes , free + , comonad default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, OverloadedStrings ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j diff --git a/src/Diff.hs b/src/Diff.hs index dca069b7d..e363f02bf 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -1,7 +1,9 @@ +{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-} module Diff where import Control.Comonad.Trans.Cofree -import Control.Monad.Free +import Control.Monad.Trans.Free +import Data.Functor.Foldable as Foldable import Data.Functor.Both import Patch import Syntax @@ -20,9 +22,13 @@ annotate :: annotation -> Syntax a f -> CofreeF (Syntax a) annotation f annotate = (:<) -- | An annotated series of patches of terms. +type DiffF a annotation = FreeF (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) +type instance Base (Diff a annotation) = DiffF a annotation +type instance Base (Free f a) = FreeF f a + +instance (Functor f) => Foldable.Foldable (Free f a) where project = runFree --- | Sum the result of a transform applied to all the patches in the diff. diffSum :: (Patch (Term a annotation) -> Integer) -> Diff a annotation -> Integer diffSum patchCost diff = sum $ fmap patchCost diff diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 37b7b4dc7..8f224f8a2 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -8,12 +8,16 @@ import Term import Syntax import qualified Data.Text as T import qualified Category +import Data.Functor.Both import Data.Monoid import Data.Maybe (listToMaybe) import Data.Set (toList) import Control.Arrow import Control.Monad +import Control.Comonad import Control.Comonad.Trans.Cofree +import Control.Monad.Trans.Free +import qualified Control.Comonad.Cofree as Cofree import Data.Functor.Foldable as Foldable @@ -30,11 +34,19 @@ patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (af where memptyOrDiff = maybe emptyDiffSummary termSummary -diffSummary :: Diff a Info -> DiffSummary -diffSummary = histo diffSummary' where - diffSummary' :: DiffF a (Cofree (DiffF a Info) DiffSummary) f -> DiffSummary - diffSummary' (coDiffSummary :< Leaf _) = diffSummary - where (diffSummary :< _) = runCofree coDiffSummary +type DiffSummaryF leaf annotation = FreeF (CofreeF (Syntax leaf) (Both annotation)) +diffSummary :: Diff leaf Info -> DiffSummary +-- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a +diffSummary = histo diffSummary' . fmap (patchSummary undefined) where + --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary + -- Skip any child that doesn't have any changes (that will always include leaves) + -- Skip any child that doesn't have any changes (that will always include leaves) + diffSummary' :: DiffSummaryF leaf annotation DiffSummary (Cofree.Cofree (DiffSummaryF leaf annotation DiffSummary) DiffSummary) -> DiffSummary + diffSummary' (Free (_ :< Leaf _)) = undefined + diffSummary' (Free (_ :< Indexed children)) = DiffSummary { diffChanges = children >>= diffChanges . extract } + diffSummary' (Free (_ :< Fixed children)) = undefined + diffSummary' (Free (_ :< Keyed children)) = undefined + diffSummary' (Pure diffSummary) = diffSummary :: DiffSummary -- (patchSummary termSummary) -- Syntax Text DiffSummary -> DiffSummary Text From 48c7498ce2a549463143ca456253a8d756b64567 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 4 May 2016 14:37:24 -0400 Subject: [PATCH 11/53] Define Term in terms of Cofree --- semantic-diff.cabal | 1 - src/Algorithm.hs | 2 +- src/Alignment.hs | 12 ++++---- src/Category.hs | 3 +- src/Control/Monad/Free.hs | 18 ------------ src/Diff.hs | 3 +- src/DiffSummary.hs | 2 -- src/Diffing.hs | 19 +++++++------ src/Interpreter.hs | 59 ++++++++++++++++++++------------------- src/Parser.hs | 6 ++-- src/Renderer/JSON.hs | 20 ++++++------- src/Renderer/Patch.hs | 7 +++-- src/Renderer/Split.hs | 6 ++-- src/SES.hs | 10 +++---- src/SplitDiff.hs | 2 +- src/Term.hs | 11 +++++--- 16 files changed, 82 insertions(+), 99 deletions(-) delete mode 100644 src/Control/Monad/Free.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index 65baf4ebd..4297f214d 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -17,7 +17,6 @@ library , Alignment , Category , Control.Comonad.CofreeExtras - , Control.Monad.Free , Data.Adjoined , Data.Align , Data.Bifunctor.These diff --git a/src/Algorithm.hs b/src/Algorithm.hs index 7bd357e6d..0fb8ae129 100644 --- a/src/Algorithm.hs +++ b/src/Algorithm.hs @@ -1,6 +1,6 @@ module Algorithm where -import Control.Monad.Free +import Control.Monad.Trans.Free import Operation -- | A lazily-produced AST for diffing. diff --git a/src/Alignment.hs b/src/Alignment.hs index 73c17d0a0..e502a7f5e 100644 --- a/src/Alignment.hs +++ b/src/Alignment.hs @@ -12,7 +12,7 @@ import Control.Arrow import Control.Comonad.Trans.Cofree import Data.Functor.Foldable hiding (Foldable) import Control.Monad -import Control.Monad.Free +import Control.Monad.Trans.Free import Data.Adjoined import Data.Align import Data.Bifunctor.These @@ -49,17 +49,17 @@ hasChanges = or . fmap (or . (True <$)) -- | Split a diff, which may span multiple lines, into rows of split diffs paired with the Range of characters spanned by that Row on each side of the diff. splitDiffByLines :: Both (Source Char) -> Diff leaf Info -> [Row (SplitDiff leaf Info, Range)] -splitDiffByLines sources = toList . iter (\ (infos :< syntax) -> splitAbstractedTerm ((Free .) . (:<)) sources (infos :< syntax)) . fmap (splitPatchByLines sources) +splitDiffByLines sources = toList . iter (\ (infos :< syntax) -> splitAbstractedTerm ((free .) . (Free .) . (:<)) sources (infos :< syntax)) . fmap (splitPatchByLines sources) -- | Split a patch, which may span multiple lines, into rows of split diffs. splitPatchByLines :: Both (Source Char) -> Patch (Term leaf Info) -> Adjoined (Both (Line (SplitDiff leaf Info, Range))) splitPatchByLines sources patch = wrapTermInPatch <$> splitAndFoldTerm (unPatch patch) where splitAndFoldTerm :: These (Term leaf Info) (Term leaf Info) -> Adjoined (Both (Line (Term leaf Info, Range))) - splitAndFoldTerm (This deleted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ fst sources)) (hylo (Fix . annotationMap Identity) unfix deleted)) nil - splitAndFoldTerm (That inserted) = tsequenceL mempty $ both nil (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ snd sources)) (hylo (Fix . annotationMap Identity) unfix inserted)) - splitAndFoldTerm (These deleted inserted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ fst sources)) (hylo (Fix . annotationMap Identity) unfix deleted)) (runIdentity <$> cata (splitAbstractedTerm ((Fix .) . (:<)) (Identity $ snd sources)) (hylo (Fix . annotationMap Identity) unfix inserted)) - wrapTermInPatch = fmap (fmap (first (Pure . constructor patch))) + splitAndFoldTerm (This deleted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((cofree.) . (:<)) (Identity $ fst sources)) (hylo (cofree . annotationMap Identity) runCofree deleted)) nil + splitAndFoldTerm (That inserted) = tsequenceL mempty $ both nil (runIdentity <$> cata (splitAbstractedTerm ((cofree .) . (:<)) (Identity $ snd sources)) (hylo (cofree . annotationMap Identity) runCofree inserted)) + splitAndFoldTerm (These deleted inserted) = tsequenceL mempty $ both (runIdentity <$> cata (splitAbstractedTerm ((cofree .) . (:<)) (Identity $ fst sources)) (hylo (cofree . annotationMap Identity) runCofree deleted)) (runIdentity <$> cata (splitAbstractedTerm ((cofree .) . (:<)) (Identity $ snd sources)) (hylo (cofree . annotationMap Identity) runCofree inserted)) + wrapTermInPatch = fmap (fmap (first (free . Pure . constructor patch))) constructor (Replace _ _) = SplitReplace constructor (Insert _) = SplitInsert constructor (Delete _) = SplitDelete diff --git a/src/Category.hs b/src/Category.hs index a975f3e48..51d33e6c1 100644 --- a/src/Category.hs +++ b/src/Category.hs @@ -3,7 +3,6 @@ module Category where import Term import Control.Comonad.Trans.Cofree -import Data.Functor.Foldable import Data.Set -- | A standardized category of AST node. Used to determine the semantics for @@ -34,7 +33,7 @@ class Categorizable a where categories :: a -> Set Category instance Categorizable annotation => Categorizable (Term a annotation) where - categories (Fix (annotation :< _)) = categories annotation + categories term | (annotation :< _) <- runCofree term = categories annotation -- | Test whether the categories from the categorizables intersect. comparable :: Categorizable a => a -> a -> Bool diff --git a/src/Control/Monad/Free.hs b/src/Control/Monad/Free.hs deleted file mode 100644 index 4c7a1271c..000000000 --- a/src/Control/Monad/Free.hs +++ /dev/null @@ -1,18 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -module Control.Monad.Free where - -data Free functor pure = Free (functor (Free functor pure)) | Pure pure - deriving (Functor, Foldable, Traversable) - -instance (Eq pure, Eq (functor (Free functor pure))) => Eq (Free functor pure) where - Pure a == Pure b = a == b - Free f == Free g = f == g - _ == _ = False - -instance (Show pure, Show (functor (Free functor pure))) => Show (Free functor pure) where - showsPrec n (Pure a) = ("Pure " ++) . showsPrec n a - showsPrec n (Free f) = ("Free " ++) . showsPrec n f - -iter :: Functor functor => (functor pure -> pure) -> Free functor pure -> pure -iter _ (Pure a) = a -iter f (Free g) = f (iter f <$> g) diff --git a/src/Diff.hs b/src/Diff.hs index e363f02bf..dee784010 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -24,9 +24,8 @@ annotate = (:<) -- | An annotated series of patches of terms. type DiffF a annotation = FreeF (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) -type instance Base (Diff a annotation) = DiffF a annotation -type instance Base (Free f a) = FreeF f a +type instance Base (Free f a) = FreeF f a instance (Functor f) => Foldable.Foldable (Free f a) where project = runFree diffSum :: (Patch (Term a annotation) -> Integer) -> Diff a annotation -> Integer diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 8f224f8a2..d0d749c04 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -12,8 +12,6 @@ import Data.Functor.Both import Data.Monoid import Data.Maybe (listToMaybe) import Data.Set (toList) -import Control.Arrow -import Control.Monad import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free diff --git a/src/Diffing.hs b/src/Diffing.hs index dc3a38b38..8cb163926 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -13,7 +13,7 @@ import Term import TreeSitter import Text.Parser.TreeSitter.Language -import Control.Monad.Free +import Control.Monad.Trans.Free import Control.Comonad.Trans.Cofree import Data.Functor.Both import Data.Functor.Foldable @@ -35,8 +35,8 @@ parserForType mediaType = case languageForType mediaType of -- | A fallback parser that treats a file simply as rows of strings. lineByLineParser :: Parser -lineByLineParser input = return . Fix . root $ case foldl' annotateLeaves ([], 0) lines of - (leaves, _) -> Fix <$> leaves +lineByLineParser input = return . cofree . root $ case foldl' annotateLeaves ([], 0) lines of + (leaves, _) -> cofree <$> leaves where lines = actualLines input root children = Info (Range 0 $ length input) mempty (1 + fromIntegral (length children)) :< Indexed children @@ -58,11 +58,11 @@ breakDownLeavesByWord source = cata replaceIn replaceIn (Info range categories _ :< Leaf _) | ranges <- rangesAndWordsInSource range , length ranges > 1 - = Fix $ Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges) + = cofree $ Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges) replaceIn (Info range categories _ :< syntax) - = Fix $ Info range categories (1 + sum (size . headF . unfix <$> syntax)) :< syntax + = cofree $ Info range categories (1 + sum (size . headF . runCofree <$> syntax)) :< syntax rangesAndWordsInSource range = rangesAndWordsFrom (start range) (toString $ slice range source) - makeLeaf categories (range, substring) = Fix $ Info range categories 1 :< Leaf (T.pack substring) + makeLeaf categories (range, substring) = cofree $ Info range categories 1 :< Leaf (T.pack substring) -- | Transcode a file to a unicode source. transcode :: B1.ByteString -> IO (Source Char) @@ -90,9 +90,10 @@ diffFiles parser renderer sourceBlobs = do -- | The sum of the node count of the diff’s patches. diffCostWithCachedTermSizes :: Diff a Info -> Integer -diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . headF . unfix)) +diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . headF . runCofree)) -- | The absolute difference between the node counts of a diff. diffCostWithAbsoluteDifferenceOfCachedDiffSizes :: Diff a Info -> Integer -diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Free (Both (before, after) :< _)) = abs $ size before - size after -diffCostWithAbsoluteDifferenceOfCachedDiffSizes (Pure patch) = sum $ size . headF . unfix <$> patch +diffCostWithAbsoluteDifferenceOfCachedDiffSizes term = case runFree term of + (Free (Both (before, after) :< _)) -> abs $ size before - size after + (Pure patch) -> sum $ size . headF . runCofree <$> patch diff --git a/src/Interpreter.hs b/src/Interpreter.hs index 479b38464..4809b333e 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -2,9 +2,10 @@ module Interpreter (interpret, Comparable, diffTerms) where import Algorithm import Category -import Control.Comonad.Trans.Cofree import Data.Functor.Foldable -import Control.Monad.Free +import Control.Comonad +import Control.Monad.Trans.Free +import Control.Comonad.Trans.Cofree import Data.Functor.Both import qualified Data.OrderedMap as Map import qualified Data.List as List @@ -28,46 +29,46 @@ diffTerms cost = interpret comparable cost -- | Diff two terms, given a function that determines whether two terms can be compared. interpret :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Diff a annotation -interpret comparable cost a b = fromMaybe (Pure $ Replace a b) $ constructAndRun comparable cost a b +interpret comparable cost a b = fromMaybe (free . Pure $ Replace a b) $ constructAndRun comparable cost a b -- | Constructs an algorithm and runs it constructAndRun :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Maybe (Diff a annotation) -constructAndRun _ _ a b | a == b = hylo (\termF -> Free $ headF termF :< tailF termF) unfix <$> zipTerms a b +constructAndRun _ _ a b | a == b = hylo (\termF -> free . Free $ headF termF :< tailF termF) runCofree <$> zipTerms a b constructAndRun comparable _ a b | not $ comparable a b = Nothing -constructAndRun comparable cost (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) = +constructAndRun comparable cost t1 t2 = run comparable cost $ algorithm a b where - algorithm (Indexed a') (Indexed b') = Free $ ByIndex a' b' (annotate . Indexed) - algorithm (Keyed a') (Keyed b') = Free $ ByKey a' b' (annotate . Keyed) + algorithm (Indexed a') (Indexed b') = free . Free $ ByIndex a' b' (annotate . Indexed) + algorithm (Keyed a') (Keyed b') = free . Free $ ByKey a' b' (annotate . Keyed) algorithm (Leaf a') (Leaf b') | a' == b' = annotate $ Leaf b' - algorithm a' b' = Free $ Recursive (Fix (annotation1 :< a')) (Fix (annotation2 :< b')) Pure - annotate = Pure . Free . (Both (annotation1, annotation2) :<) + algorithm a' b' = free . Free $ Recursive (cofree (annotation1 :< a')) (cofree (annotation2 :< b')) (free . Pure) + (annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2) + annotate = free . Pure . free . Free . Diff.annotate (both annotation1 annotation2) -- | Runs the diff algorithm run :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation) -run _ _ (Pure diff) = Just diff +run comparable cost algorithm = case runFree algorithm of + (Pure diff) -> Just diff + (Free (Recursive t1 t2 f)) -> run comparable cost . f $ recur a b where + (annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2) + annotate = free . Free . (Both (annotation1, annotation2) :<) -run comparable cost (Free (Recursive (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) f)) = run comparable cost . f $ recur a b where - recur (Indexed a') (Indexed b') | length a' == length b' = annotate . Indexed $ zipWith (interpret comparable cost) a' b' - recur (Fixed a') (Fixed b') | length a' == length b' = annotate . Fixed $ zipWith (interpret comparable cost) a' b' - recur (Keyed a') (Keyed b') | Map.keys a' == bKeys = annotate . Keyed . Map.fromList . fmap repack $ bKeys - where + recur (Indexed a') (Indexed b') | length a' == length b' = annotate . Indexed $ zipWith (interpret comparable cost) a' b' + recur (Fixed a') (Fixed b') | length a' == length b' = annotate . Fixed $ zipWith (interpret comparable cost) a' b' + recur (Keyed a') (Keyed b') | Map.keys a' == bKeys = annotate . Keyed . Map.fromList . fmap repack $ bKeys where bKeys = Map.keys b' repack key = (key, interpretInBoth key a' b') interpretInBoth key x y = interpret comparable cost (x ! key) (y ! key) - recur _ _ = Pure $ Replace (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) + recur _ _ = free . Pure $ Replace (cofree (annotation1 :< a)) (cofree (annotation2 :< b)) + Free (ByKey a b f) -> run comparable cost $ f byKey where + byKey = Map.fromList $ toKeyValue <$> List.union aKeys bKeys + toKeyValue key | key `List.elem` deleted = (key, free . Pure . Delete $ a ! key) + toKeyValue key | key `List.elem` inserted = (key, free . Pure . Insert $ b ! key) + toKeyValue key = (key, interpret comparable cost (a ! key) (b ! key)) + aKeys = Map.keys a + bKeys = Map.keys b + deleted = aKeys \\ bKeys + inserted = bKeys \\ aKeys + Free (ByIndex a b f) -> run comparable cost . f $ ses (constructAndRun comparable cost) cost a b - annotate = Free . (Both (annotation1, annotation2) :<) - -run comparable cost (Free (ByKey a b f)) = run comparable cost $ f byKey where - byKey = Map.fromList $ toKeyValue <$> List.union aKeys bKeys - toKeyValue key | List.elem key deleted = (key, Pure . Delete $ a ! key) - toKeyValue key | List.elem key inserted = (key, Pure . Insert $ b ! key) - toKeyValue key = (key, interpret comparable cost (a ! key) (b ! key)) - aKeys = Map.keys a - bKeys = Map.keys b - deleted = aKeys \\ bKeys - inserted = bKeys \\ aKeys - -run comparable cost (Free (ByIndex a b f)) = run comparable cost . f $ ses (constructAndRun comparable cost) cost a b diff --git a/src/Parser.hs b/src/Parser.hs index 6220b6dce..04f112db2 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -40,7 +40,7 @@ isFixed = not . Set.null . Set.intersection fixedCategories -- | Given a function that maps production names to sets of categories, produce -- | a Constructor. termConstructor :: (String -> Set.Set Category) -> Constructor -termConstructor mapping source range name children = Fix (Info range categories (1 + sum (size . headF . unfix <$> children)) :< construct children) +termConstructor mapping source range name children = cofree (Info range categories (1 + sum (size . headF . runCofree <$> children)) :< construct children) where categories = mapping name construct :: [Term Text Info] -> Syntax Text (Term Text Info) @@ -48,6 +48,6 @@ termConstructor mapping source range name children = Fix (Info range categories construct children | isFixed categories = Fixed children construct children | isKeyed categories = Keyed . Map.fromList $ assignKey <$> children construct children = Indexed children - assignKey node@(Fix (Info _ categories _ :< Fixed (key : _))) | Set.member Pair categories = (getSubstring key, node) + assignKey node | Info _ categories _ :< Fixed (key : _) <- runCofree node, Set.member Pair categories = (getSubstring key, node) assignKey node = (getSubstring node, node) - getSubstring (Fix (Info range _ _ :< _)) = pack . toString $ slice range source + getSubstring term | Info range _ _ :< _ <- runCofree term = pack . toString $ slice range source diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index 6dbed185d..e720ad99e 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -7,8 +7,7 @@ module Renderer.JSON ( import Alignment import Category import Control.Comonad.Trans.Cofree -import Data.Functor.Foldable -import Control.Monad.Free +import Control.Monad.Trans.Free import Data.Aeson hiding (json) import Data.Aeson.Encode import Data.Functor.Both @@ -17,7 +16,6 @@ import Data.Text.Lazy (toStrict) import Data.Text.Lazy.Builder (toLazyText) import qualified Data.Text as T import Data.Vector hiding (toList) -import Diff import Info import Line import Range @@ -47,16 +45,18 @@ instance ToJSON a => ToJSON (Both a) where toJSON (Both (a, b)) = Array . fromList $ toJSON <$> [ a, b ] toEncoding = foldable instance ToJSON (SplitDiff leaf Info) where - toJSON (Free (info :< syntax)) = object (termFields info syntax) - toJSON (Pure patch) = object (patchFields patch) - toEncoding (Free (info :< syntax)) = pairs $ mconcat (termFields info syntax) - toEncoding (Pure patch) = pairs $ mconcat (patchFields patch) + toJSON splitDiff = case runFree splitDiff of + (Free (info :< syntax)) -> object (termFields info syntax) + (Pure patch) -> object (patchFields patch) + toEncoding splitDiff = case runFree splitDiff of + (Free (info :< syntax)) -> pairs $ mconcat (termFields info syntax) + (Pure patch) -> pairs $ mconcat (patchFields patch) instance ToJSON value => ToJSON (OrderedMap T.Text value) where toJSON map = object $ uncurry (.=) <$> toList map toEncoding map = pairs . mconcat $ uncurry (.=) <$> toList map instance ToJSON (Term leaf Info) where - toJSON (Fix (info :< syntax)) = object (termFields info syntax) - toEncoding (Fix (info :< syntax)) = pairs $ mconcat (termFields info syntax) + toJSON term | (info :< syntax) <- runCofree term = object (termFields info syntax) + toEncoding term | (info :< syntax) <- runCofree term = pairs $ mconcat (termFields info syntax) lineFields :: KeyValue kv => Int -> Line (SplitDiff leaf Info, Range) -> [kv] lineFields n line | isEmpty line = [] @@ -79,4 +79,4 @@ patchFields patch = case patch of SplitInsert term -> fields "insert" term SplitDelete term -> fields "delete" term SplitReplace term -> fields "replace" term - where fields kind (Fix (info :< syntax)) = "patch" .= T.pack kind : termFields info syntax + where fields kind term | (info :< syntax) <- runCofree term = "patch" .= T.pack kind : termFields info syntax diff --git a/src/Renderer/Patch.hs b/src/Renderer/Patch.hs index 742694997..c00d27b30 100644 --- a/src/Renderer/Patch.hs +++ b/src/Renderer/Patch.hs @@ -17,7 +17,7 @@ import Source hiding ((++), break) import SplitDiff import Control.Comonad.Trans.Cofree import Data.Functor.Foldable -import Control.Monad.Free +import Control.Monad.Trans.Free import Data.Functor.Both as Both import Data.List import Data.Maybe @@ -86,8 +86,9 @@ showLine source line | isEmpty line = Nothing -- | Return the range from a split diff. getRange :: SplitDiff leaf Info -> Range -getRange (Free (Info range _ _ :< _)) = range -getRange (Pure patch) = let (Fix (Info range _ _ :< _)) = getSplitTerm patch in range +getRange splitDiff = case runFree splitDiff of + (Free (Info range _ _ :< _)) -> range + (Pure patch) -> range where (Info range _ _ :< _) = runCofree $ getSplitTerm patch -- | Returns the header given two source blobs and a hunk. header :: Both SourceBlob -> String diff --git a/src/Renderer/Split.hs b/src/Renderer/Split.hs index 2b08225db..33ff75bf0 100644 --- a/src/Renderer/Split.hs +++ b/src/Renderer/Split.hs @@ -4,7 +4,7 @@ module Renderer.Split where import Alignment import Category import Control.Comonad.Trans.Cofree -import Control.Monad.Free +import Control.Monad.Trans.Free import Data.Foldable import Data.Functor.Both import Data.Functor.Foldable @@ -108,8 +108,8 @@ instance ToMarkup (Renderable (Source Char, Term a Info)) where instance ToMarkup (Renderable (Source Char, SplitDiff a Info)) where toMarkup (Renderable (source, diff)) = Prelude.fst $ iter (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) $ toMarkupAndRange <$> diff where toMarkupAndRange :: SplitPatch (Term a Info) -> (Markup, Range) - toMarkupAndRange patch = let term@(Fix (Info range _ _ :< _)) = getSplitTerm patch in - ((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue . show $ termSize term)) . toMarkup $ Renderable (source, term), range) + toMarkupAndRange patch = let term@(Info range _ _ :< _) = runCofree $ getSplitTerm patch in + ((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue . show . termSize $ cofree term)) . toMarkup $ Renderable (source, cofree term), range) instance ToMarkup a => ToMarkup (Renderable (Bool, Int, Line a)) where diff --git a/src/SES.hs b/src/SES.hs index 1a5277fec..2519435bd 100644 --- a/src/SES.hs +++ b/src/SES.hs @@ -3,7 +3,7 @@ module SES where import Patch import Diff import Term -import Control.Monad.Free +import Control.Monad.Trans.Free import Control.Monad.State import Data.Foldable (minimumBy) import Data.List (uncons) @@ -25,9 +25,9 @@ ses diffTerms cost as bs = fst <$> evalState diffState Map.empty where diffAt :: Compare a annotation -> Cost a annotation -> (Integer, Integer) -> [Term a annotation] -> [Term a annotation] -> State (Map.Map (Integer, Integer) [(Diff a annotation, Integer)]) [(Diff a annotation, Integer)] diffAt _ _ _ [] [] = return [] diffAt _ cost _ [] bs = return $ foldr toInsertions [] bs where - toInsertions each = consWithCost cost (Pure . Insert $ each) + toInsertions each = consWithCost cost (free . Pure . Insert $ each) diffAt _ cost _ as [] = return $ foldr toDeletions [] as where - toDeletions each = consWithCost cost (Pure . Delete $ each) + toDeletions each = consWithCost cost (free . Pure . Delete $ each) diffAt diffTerms cost (i, j) (a : as) (b : bs) = do cachedDiffs <- get case Map.lookup (i, j) cachedDiffs of @@ -44,8 +44,8 @@ diffAt diffTerms cost (i, j) (a : as) (b : bs) = do put $ Map.insert (i, j) nomination cachedDiffs' return nomination where - delete = consWithCost cost (Pure . Delete $ a) - insert = consWithCost cost (Pure . Insert $ b) + delete = consWithCost cost (free . Pure . Delete $ a) + insert = consWithCost cost (free . Pure . Insert $ b) costOf [] = 0 costOf ((_, c) : _) = c best = minimumBy (comparing costOf) diff --git a/src/SplitDiff.hs b/src/SplitDiff.hs index 87d5f3fb8..6decb2a88 100644 --- a/src/SplitDiff.hs +++ b/src/SplitDiff.hs @@ -1,7 +1,7 @@ module SplitDiff where import Syntax -import Control.Monad.Free (Free) +import Control.Monad.Trans.Free (Free) import Control.Comonad.Trans.Cofree import Term (Term) diff --git a/src/Term.hs b/src/Term.hs index bd2eb9444..dd4415220 100644 --- a/src/Term.hs +++ b/src/Term.hs @@ -13,15 +13,18 @@ unfix (Fix f) = f -- | An annotated node (Syntax) in an abstract syntax tree. type TermF a annotation = CofreeF (Syntax a) annotation -type Term a annotation = Fix (TermF a annotation) -type instance Base (Term a annotation) = (TermF a annotation) +type Term a annotation = Cofree (Syntax a) annotation + +type instance Base (Cofree f a) = CofreeF f a +instance Functor f => Foldable.Foldable (Cofree f a) where project = runCofree -- | Zip two terms by combining their annotations into a pair of annotations. -- | If the structure of the two terms don't match, then Nothing will be returned. zipTerms :: Term a annotation -> Term a annotation -> Maybe (Term a (Both annotation)) -zipTerms (Fix (annotation1 :< a)) (Fix (annotation2 :< b)) = annotate $ zipUnwrap a b +zipTerms t1 t2 = annotate (zipUnwrap a b) where - annotate = fmap (Fix . (Both (annotation1, annotation2) :<)) + (annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2) + annotate = fmap (cofree . (Both (annotation1, annotation2) :<)) zipUnwrap (Leaf _) (Leaf b') = Just $ Leaf b' zipUnwrap (Indexed a') (Indexed b') = Just . Indexed . catMaybes $ zipWith zipTerms a' b' zipUnwrap (Fixed a') (Fixed b') = Just . Fixed . catMaybes $ zipWith zipTerms a' b' From 170110ee8e774466602e9a3f357bee76695e2724 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 4 May 2016 15:15:25 -0400 Subject: [PATCH 12/53] Fix up tests --- semantic-diff.cabal | 2 ++ src/Term.hs | 1 + test/AlignmentSpec.hs | 20 ++++++++++---------- test/ArbitraryTerm.hs | 5 +++-- test/InterpreterSpec.hs | 12 ++++++------ test/PatchOutputSpec.hs | 6 +++--- 6 files changed, 25 insertions(+), 21 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index 4297f214d..baf2ed7a4 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -91,6 +91,8 @@ test-suite semantic-diff-test , quickcheck-text , semantic-diff , text >= 1.2.1.3 + , free + , recursion-schemes >= 4.1 if os(darwin) ghc-options: -threaded -rtsopts -with-rtsopts=-N -j else diff --git a/src/Term.hs b/src/Term.hs index dd4415220..5e77cdcb8 100644 --- a/src/Term.hs +++ b/src/Term.hs @@ -17,6 +17,7 @@ type Term a annotation = Cofree (Syntax a) annotation type instance Base (Cofree f a) = CofreeF f a instance Functor f => Foldable.Foldable (Cofree f a) where project = runCofree +instance Functor f => Foldable.Unfoldable (Cofree f a) where embed = cofree -- | Zip two terms by combining their annotations into a pair of annotations. -- | If the structure of the two terms don't match, then Nothing will be returned. diff --git a/test/AlignmentSpec.hs b/test/AlignmentSpec.hs index fb4cbac84..2204cc639 100644 --- a/test/AlignmentSpec.hs +++ b/test/AlignmentSpec.hs @@ -8,8 +8,8 @@ import Data.Text.Arbitrary () import Alignment import ArbitraryTerm (arbitraryLeaf) import Control.Arrow -import Control.Comonad.Cofree -import Control.Monad.Free hiding (unfold) +import Control.Comonad.Trans.Cofree +import Control.Monad.Trans.Free hiding (unfold) import Data.Adjoined import Data.Copointed import Data.Functor.Both as Both @@ -32,34 +32,34 @@ spec = parallel $ do describe "splitDiffByLines" $ do prop "preserves line counts in equal sources" $ \ source -> - length (splitDiffByLines (pure source) (Free $ Annotated (pure $ Info (totalRange source) mempty 1) (Indexed . Prelude.fst $ foldl combineIntoLeaves ([], 0) source))) `shouldBe` length (filter (== '\n') $ toString source) + 1 + length (splitDiffByLines (pure source) (free . Free $ (pure $ Info (totalRange source) mempty 1) :< (Indexed . Prelude.fst $ foldl combineIntoLeaves ([], 0) source))) `shouldBe` length (filter (== '\n') $ toString source) + 1 prop "produces the maximum line count in inequal sources" $ \ sources -> let ranges = actualLineRanges <$> (totalRange <$> sources) <*> sources in - length (splitDiffByLines sources (Free $ Annotated ((\ s -> Info (totalRange s) mempty 0) <$> sources) (Indexed $ leafWithRangesInSources sources <$> runBothWith (zipWith both) ranges))) `shouldBe` runBothWith max ((+ 1) . length . filter (== '\n') . toString <$> sources) + length (splitDiffByLines sources (free . Free $ ((\ s -> Info (totalRange s) mempty 0) <$> sources) :< (Indexed $ leafWithRangesInSources sources <$> runBothWith (zipWith both) ranges))) `shouldBe` runBothWith max ((+ 1) . length . filter (== '\n') . toString <$> sources) describe "splitAbstractedTerm" $ do prop "preserves line count" $ \ source -> let range = totalRange source in - splitAbstractedTerm (:<) (Identity source) (Identity (Info range mempty 0)) (Leaf source) `shouldBe` (Identity . lineMap (fmap (((:< Leaf source) . (\ r -> Info r mempty 0) &&& id))) <$> linesInRangeOfSource range source) + splitAbstractedTerm ((cofree .) . (:<)) (Identity source) (Identity (Info range mempty 0) :< Leaf source) `shouldBe` (Identity . lineMap (fmap (cofree . (:< Leaf source) . (\ r -> Info r mempty 0) &&& id)) <$> linesInRangeOfSource range source) - let makeTerm = ((Free .) . Annotated) :: Info -> Syntax (Source Char) (SplitDiff (Source Char) Info) -> SplitDiff (Source Char) Info + let makeTerm = ((free .) . (Free .) . (:<)) :: Info -> Syntax (Source Char) (SplitDiff (Source Char) Info) -> SplitDiff (Source Char) Info prop "outputs one row for single-line unchanged leaves" $ forAll (arbitraryLeaf `suchThat` isOnSingleLine) $ - \ (source, (Info range categories _), syntax) -> splitAbstractedTerm makeTerm (pure source) (pure $ Info range categories 0) syntax `shouldBe` fromList [ + \ (source, (Info range categories _), syntax) -> splitAbstractedTerm makeTerm (pure source) (pure (Info range categories 0) :< syntax) `shouldBe` fromList [ both (pure (makeTerm (Info range categories 0) $ Leaf source, Range 0 (length source))) (pure (makeTerm (Info range categories 0) $ Leaf source, Range 0 (length source))) ] prop "outputs one row for single-line empty unchanged indexed nodes" $ forAll (arbitrary `suchThat` (\ a -> filter (/= '\n') (toString a) == toString a)) $ - \ source -> splitAbstractedTerm makeTerm (pure source) (pure $ Info (totalRange source) mempty 0) (Indexed []) `shouldBe` fromList [ + \ source -> splitAbstractedTerm makeTerm (pure source) (pure (Info (totalRange source) mempty 0) :< Indexed []) `shouldBe` fromList [ both (pure (makeTerm (Info (totalRange source) mempty 0) $ Indexed [], Range 0 (length source))) (pure (makeTerm (Info (totalRange source) mempty 0) $ Indexed [], Range 0 (length source))) ] where isOnSingleLine (a, _, _) = filter (/= '\n') (toString a) == toString a - combineIntoLeaves (leaves, start) char = (leaves ++ [ Free $ Annotated (Info <$> pure (Range start $ start + 1) <*> mempty <*> pure 1) (Leaf [ char ]) ], start + 1) + combineIntoLeaves (leaves, start) char = (leaves ++ [ free . Free $ (Info <$> pure (Range start $ start + 1) <*> mempty <*> pure 1) :< Leaf [ char ] ], start + 1) - leafWithRangesInSources sources ranges = Free $ Annotated (Info <$> ranges <*> pure mempty <*> pure 1) (Leaf $ runBothWith (++) (toString <$> sources)) + leafWithRangesInSources sources ranges = free . Free $ (Info <$> ranges <*> pure mempty <*> pure 1) :< (Leaf $ runBothWith (++) (toString <$> sources)) leafWithRangeInSource source range = Info range mempty 1 :< Leaf source diff --git a/test/ArbitraryTerm.hs b/test/ArbitraryTerm.hs index 08fb7fd79..b83416e89 100644 --- a/test/ArbitraryTerm.hs +++ b/test/ArbitraryTerm.hs @@ -1,7 +1,8 @@ module ArbitraryTerm where import Category -import Control.Comonad.Cofree +import Control.Comonad.Trans.Cofree +import Data.Functor.Foldable import Control.Monad import Data.Functor.Both import qualified Data.OrderedMap as Map @@ -25,7 +26,7 @@ newtype ArbitraryTerm a annotation = ArbitraryTerm (annotation, Syntax a (Arbitr unTerm :: ArbitraryTerm a annotation -> Term a annotation unTerm = unfold unpack - where unpack (ArbitraryTerm (annotation, syntax)) = (annotation, syntax) + where unpack (ArbitraryTerm (annotation, syntax)) = annotation :< syntax instance (Eq a, Eq annotation, Arbitrary a, Arbitrary annotation) => Arbitrary (ArbitraryTerm a annotation) where arbitrary = scale (`div` 2) $ sized (\ x -> boundedTerm x x) -- first indicates the cube of the max length of lists, second indicates the cube of the max depth of the tree diff --git a/test/InterpreterSpec.hs b/test/InterpreterSpec.hs index afdec92ff..79921c222 100644 --- a/test/InterpreterSpec.hs +++ b/test/InterpreterSpec.hs @@ -4,19 +4,19 @@ import Diff import qualified Interpreter as I import Range import Syntax -import Control.Comonad.Cofree -import Control.Monad.Free +import Control.Comonad.Trans.Cofree +import Control.Monad.Trans.Free import Patch import Info import Category import Test.Hspec spec :: Spec -spec = parallel $ do - describe "interpret" $ do +spec = parallel $ + describe "interpret" $ it "returns a replacement when comparing two unicode equivalent terms" $ - I.interpret comparable diffCost (Info range mempty 0 :< Leaf "t\776") (Info range2 mempty 0 :< Leaf "\7831") `shouldBe` - Pure (Replace (Info range mempty 0 :< Leaf "t\776") (Info range2 mempty 0 :< Leaf "\7831")) + I.interpret comparable diffCost (cofree (Info range mempty 0 :< Leaf "t\776")) (cofree (Info range2 mempty 0 :< Leaf "\7831")) `shouldBe` + free (Pure (Replace (cofree (Info range mempty 0 :< Leaf "t\776")) (cofree (Info range2 mempty 0 :< Leaf "\7831")))) where range = Range 0 2 diff --git a/test/PatchOutputSpec.hs b/test/PatchOutputSpec.hs index 1e1686de3..cfbf616ca 100644 --- a/test/PatchOutputSpec.hs +++ b/test/PatchOutputSpec.hs @@ -1,8 +1,8 @@ module PatchOutputSpec where -import Control.Monad.Free +import Control.Comonad.Trans.Cofree +import Control.Monad.Trans.Free import Data.Functor.Both -import Diff import Info import Range import Renderer.Patch @@ -14,4 +14,4 @@ spec :: Spec spec = parallel $ describe "hunks" $ it "empty diffs have empty hunks" $ - hunks (Free . Annotated (pure (Info (Range 0 0) mempty 1)) $ Leaf "") (Both (SourceBlob (fromList "") "abcde" "path2.txt" (Just defaultPlainBlob), SourceBlob (fromList "") "xyz" "path2.txt" (Just defaultPlainBlob))) `shouldBe` [Hunk {offset = Both (0, 0), changes = [], trailingContext = []}] + hunks (free . Free $ pure (Info (Range 0 0) mempty 1) :< Leaf "") (Both (SourceBlob (fromList "") "abcde" "path2.txt" (Just defaultPlainBlob), SourceBlob (fromList "") "xyz" "path2.txt" (Just defaultPlainBlob))) `shouldBe` [Hunk {offset = Both (0, 0), changes = [], trailingContext = []}] From 14a405d8c8817b6be32e25c3e5ec3de3db00d028 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 5 May 2016 20:09:50 -0400 Subject: [PATCH 13/53] Return a diff summary from termToSummary and make DiffSummary either be a DiffSummary of before and after summaries, a TermSummary, or an EmptySummary --- src/DiffSummary.hs | 58 +++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index d0d749c04..a5a3b9ff2 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE GeneralizedNewtypeDeriving, DataKinds, RecordWildCards #-} module DiffSummary where import Diff @@ -6,26 +6,46 @@ import Info import Patch import Term import Syntax -import qualified Data.Text as T import qualified Category import Data.Functor.Both import Data.Monoid import Data.Maybe (listToMaybe) import Data.Set (toList) -import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import qualified Control.Comonad.Cofree as Cofree import Data.Functor.Foldable as Foldable - -newtype DiffSummary = DiffSummary { diffChanges :: [Patch DiffEntry] } +newtype DiffContext = DiffContext { modules :: [String] } deriving (Monoid) -emptyDiffSummary :: DiffSummary -emptyDiffSummary = DiffSummary { diffChanges = [] } +data DiffSummary = DiffSummary { beforeSummary :: DiffSummary, afterSummary :: DiffSummary, context :: Maybe DiffContext } | TermSummary { termName :: String } | EmptySummary -newtype DiffEntry = DiffEntry { termName :: String } + -- T { 1 } + -- T { 2 } + -- T {1, 2} + -- T {'array', 'dictionary'} + -- T {Nothing, 'dictionary'} + + -- Given two (Both (Maybe String)) + -- TermSummary { name1 :: Maybe String, name2 :: Maybe String } + -- TermSummary { name1 = Just "1", name2 :: Just "2" } + -- DiffSummary { beforeSummary = (TermSummary { name1 = Just "1"}), afterSummary = (TermSummary { name = Just "2"}), diffContext = Nothing } + +instance (Monoid DiffSummary) where + mempty = EmptySummary + mappend EmptySummary EmptySummary = EmptySummary + mappend EmptySummary summary@TermSummary{..} = summary + mappend s@TermSummary{} EmptySummary = s + mappend EmptySummary summary@DiffSummary{..} = summary + mappend s1@TermSummary{} s2@TermSummary{} = DiffSummary { beforeSummary = s1, afterSummary = s2, context = Nothing } + mappend s@TermSummary{} DiffSummary{..} = DiffSummary { beforeSummary = mappend s beforeSummary, afterSummary = afterSummary, context = context } + mappend DiffSummary{..} s@TermSummary{} = DiffSummary { beforeSummary = beforeSummary, afterSummary = mappend afterSummary s, context = context } + mappend summary@DiffSummary{} EmptySummary = summary + mappend s1@DiffSummary{} s2@DiffSummary{} = DiffSummary { beforeSummary = mappend (beforeSummary s1) (beforeSummary s2), afterSummary = mappend (afterSummary s1) (afterSummary s2), context = mappend (context s1) (context s2) } + +emptyDiffSummary :: DiffSummary +emptyDiffSummary = EmptySummary patchSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (after patch) @@ -33,15 +53,15 @@ patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (af memptyOrDiff = maybe emptyDiffSummary termSummary type DiffSummaryF leaf annotation = FreeF (CofreeF (Syntax leaf) (Both annotation)) + diffSummary :: Diff leaf Info -> DiffSummary -- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a -diffSummary = histo diffSummary' . fmap (patchSummary undefined) where +diffSummary = histo diffSummary' . fmap (patchSummary termToSummary) where --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary -- Skip any child that doesn't have any changes (that will always include leaves) - -- Skip any child that doesn't have any changes (that will always include leaves) diffSummary' :: DiffSummaryF leaf annotation DiffSummary (Cofree.Cofree (DiffSummaryF leaf annotation DiffSummary) DiffSummary) -> DiffSummary diffSummary' (Free (_ :< Leaf _)) = undefined - diffSummary' (Free (_ :< Indexed children)) = DiffSummary { diffChanges = children >>= diffChanges . extract } + diffSummary' (Free (_ :< Indexed children)) = DiffSummary {} diffSummary' (Free (_ :< Fixed children)) = undefined diffSummary' (Free (_ :< Keyed children)) = undefined diffSummary' (Pure diffSummary) = diffSummary :: DiffSummary @@ -51,16 +71,16 @@ diffSummary = histo diffSummary' . fmap (patchSummary undefined) where -- If termSummary returns a DiffEntry that just contains the term name, we need to -- Instead of foldMap we need a histomorphism -termSummary :: Term T.Text Info -> T.Text -termSummary = Foldable.cata summary where - summary :: TermF T.Text Info f -> T.Text - summary (info :< Leaf replace) = replace +termToSummary :: Term leaf Info -> DiffSummary +termToSummary = Foldable.cata summary where + summary :: TermF leaf Info f -> DiffSummary + summary (info :< Leaf replace) = toCategory info summary (info :< Indexed children) = toCategory info summary (info :< Fixed children) = toCategory info summary (info :< Keyed _) = toCategory info - toCategory :: Info -> T.Text - toCategory term = T.pack $ case maybeFirstCategory term of - Just category -> show category - Nothing -> "Unknown" + toCategory :: Info -> DiffSummary + toCategory term = case maybeFirstCategory term of + Just category -> TermSummary { termName = show category } + Nothing -> EmptySummary maybeFirstCategory term = listToMaybe . toList $ Category.categories term From 3160c8c464f9a134d19f3051bc4fadbd6ac59ec6 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 9 May 2016 15:00:15 -0400 Subject: [PATCH 14/53] s/patchSummary/patchToSummary --- src/DiffSummary.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index a5a3b9ff2..4abfeeb1f 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -47,8 +47,8 @@ instance (Monoid DiffSummary) where emptyDiffSummary :: DiffSummary emptyDiffSummary = EmptySummary -patchSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary -patchSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (after patch) +patchToSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary +patchToSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (after patch) where memptyOrDiff = maybe emptyDiffSummary termSummary @@ -56,7 +56,7 @@ type DiffSummaryF leaf annotation = FreeF (CofreeF (Syntax leaf) (Both annotatio diffSummary :: Diff leaf Info -> DiffSummary -- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a -diffSummary = histo diffSummary' . fmap (patchSummary termToSummary) where +diffSummary = histo diffSummary' . fmap (patchToSummary termToSummary) where --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary -- Skip any child that doesn't have any changes (that will always include leaves) diffSummary' :: DiffSummaryF leaf annotation DiffSummary (Cofree.Cofree (DiffSummaryF leaf annotation DiffSummary) DiffSummary) -> DiffSummary From 1503381bed9845f33a6d3ace9b5dc54bc4a85dbd Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 10 May 2016 17:47:03 -0400 Subject: [PATCH 15/53] concat indexed and fixed diff summaries --- src/DiffSummary.hs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 4abfeeb1f..a420291c1 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,6 +1,7 @@ {-# LANGUAGE GeneralizedNewtypeDeriving, DataKinds, RecordWildCards #-} module DiffSummary where +import Prelude hiding (fst, snd) import Diff import Info import Patch @@ -11,6 +12,7 @@ import Data.Functor.Both import Data.Monoid import Data.Maybe (listToMaybe) import Data.Set (toList) +import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import qualified Control.Comonad.Cofree as Cofree @@ -59,10 +61,10 @@ diffSummary :: Diff leaf Info -> DiffSummary diffSummary = histo diffSummary' . fmap (patchToSummary termToSummary) where --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary -- Skip any child that doesn't have any changes (that will always include leaves) - diffSummary' :: DiffSummaryF leaf annotation DiffSummary (Cofree.Cofree (DiffSummaryF leaf annotation DiffSummary) DiffSummary) -> DiffSummary - diffSummary' (Free (_ :< Leaf _)) = undefined - diffSummary' (Free (_ :< Indexed children)) = DiffSummary {} - diffSummary' (Free (_ :< Fixed children)) = undefined + diffSummary' :: DiffSummaryF leaf Info DiffSummary (Cofree.Cofree (DiffSummaryF leaf Info DiffSummary) DiffSummary) -> DiffSummary + diffSummary' (Free (info :< Leaf _)) = toCategory (fst info) <> toCategory (snd info) + diffSummary' (Free (_ :< Indexed children)) = mconcat $ extract <$> children + diffSummary' (Free (_ :< Fixed children)) = mconcat $ extract <$> children diffSummary' (Free (_ :< Keyed children)) = undefined diffSummary' (Pure diffSummary) = diffSummary :: DiffSummary -- (patchSummary termSummary) @@ -79,8 +81,9 @@ termToSummary = Foldable.cata summary where summary (info :< Fixed children) = toCategory info summary (info :< Keyed _) = toCategory info - toCategory :: Info -> DiffSummary - toCategory term = case maybeFirstCategory term of - Just category -> TermSummary { termName = show category } - Nothing -> EmptySummary - maybeFirstCategory term = listToMaybe . toList $ Category.categories term +maybeFirstCategory term = listToMaybe . toList $ Category.categories term + +toCategory :: Info -> DiffSummary +toCategory info = case maybeFirstCategory info of + Just category -> TermSummary { termName = show category } + Nothing -> EmptySummary From 9d6701937666a6082c5500b8e42e1891de5ec081 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 10 May 2016 17:48:55 -0400 Subject: [PATCH 16/53] Explicit type to maybeFirstCategory --- src/DiffSummary.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index a420291c1..f6a0e449c 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -7,7 +7,7 @@ import Info import Patch import Term import Syntax -import qualified Category +import Category as Category import Data.Functor.Both import Data.Monoid import Data.Maybe (listToMaybe) @@ -81,6 +81,7 @@ termToSummary = Foldable.cata summary where summary (info :< Fixed children) = toCategory info summary (info :< Keyed _) = toCategory info +maybeFirstCategory :: (Categorizable a) => a -> Maybe Category maybeFirstCategory term = listToMaybe . toList $ Category.categories term toCategory :: Info -> DiffSummary From a5b838d4581582bb59f10e1f578208118781ec43 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 10 May 2016 17:50:05 -0400 Subject: [PATCH 17/53] Remove CofreeExtras --- semantic-diff.cabal | 1 - src/Control/Comonad/CofreeExtras.hs | 11 ----------- 2 files changed, 12 deletions(-) delete mode 100644 src/Control/Comonad/CofreeExtras.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index baf2ed7a4..cca89e4d6 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -16,7 +16,6 @@ library exposed-modules: Algorithm , Alignment , Category - , Control.Comonad.CofreeExtras , Data.Adjoined , Data.Align , Data.Bifunctor.These diff --git a/src/Control/Comonad/CofreeExtras.hs b/src/Control/Comonad/CofreeExtras.hs deleted file mode 100644 index 5121e4237..000000000 --- a/src/Control/Comonad/CofreeExtras.hs +++ /dev/null @@ -1,11 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -module Control.Comonad.CofreeExtras where - -import Control.Comonad.Cofree -import Data.Copointed - -unwrap :: Cofree functor annotation -> functor (Cofree functor annotation) -unwrap (_ :< f) = f - -instance Copointed (Cofree functor) where - copoint (annotation :< _) = annotation From 8be702f5e6f388d2d810eeb83df605832f092d1d Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 13 May 2016 11:44:03 -0400 Subject: [PATCH 18/53] stub futu --- src/Diff.hs | 1 + src/DiffSummary.hs | 162 +++++++++++++++++++++++++++++++++------------ 2 files changed, 119 insertions(+), 44 deletions(-) diff --git a/src/Diff.hs b/src/Diff.hs index dee784010..903a318ed 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -27,6 +27,7 @@ type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Ter type instance Base (Free f a) = FreeF f a instance (Functor f) => Foldable.Foldable (Free f a) where project = runFree +instance (Functor f) => Foldable.Unfoldable (Free f a) where embed = free diffSum :: (Patch (Term a annotation) -> Integer) -> Diff a annotation -> Integer diffSum patchCost diff = sum $ fmap patchCost diff diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index f6a0e449c..6762cf0e4 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving, DataKinds, RecordWildCards #-} +{-# LANGUAGE GeneralizedNewtypeDeriving, DataKinds, RecordWildCards, TypeFamilies #-} module DiffSummary where import Prelude hiding (fst, snd) @@ -7,6 +7,7 @@ import Info import Patch import Term import Syntax +import qualified Range as R import Category as Category import Data.Functor.Both import Data.Monoid @@ -15,14 +16,83 @@ import Data.Set (toList) import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free +import qualified Control.Monad.Free as Free import qualified Control.Comonad.Cofree as Cofree import Data.Functor.Foldable as Foldable +import Control.Monad.State hiding (sequence) +import qualified Data.Map as M +import Data.Functor.Identity -newtype DiffContext = DiffContext { modules :: [String] } - deriving (Monoid) +instance Unfoldable (DiffSummary a) where + embed (DiffSummary x y) = (DDiffSummary x y) + embed (TermSummary s b f) = DTermSummary s b + embed EmptySummary = DEmptySummary -data DiffSummary = DiffSummary { beforeSummary :: DiffSummary, afterSummary :: DiffSummary, context :: Maybe DiffContext } | TermSummary { termName :: String } | EmptySummary + apo f a = case f a of + Cons x (Left xs) -> x : xs + Cons x (Right b) -> x : apo f b + Nil -> [] + +-- * -- +-- -- * + +data DiffSummary a = BranchSummary [DiffSummary a] + | TermSummary String a + | EmptySummary + deriving (Eq, Show, Functor, Ord) + +data instance Prim (DiffSummary a) b = PBranchSummary (Prim [a] b) b | PTermSummary String a b | PEmptySummary + +type instance Base (DiffSummary a) = Prim (DiffSummary a) +instance Foldable.Foldable (DiffSummary a) where project = Const +instance Unfoldable (DiffSummary a) where embed = getConst + +-- data DiffSummary' = [(String, [String])] + +-- data DiffSummary'' = DiffSummaryForPatch'' String | DiffSummaryForBranch'' String [DiffSummary''] + +-- x = DiffSummaryForBranch'' "module Foo" [ DiffSummaryForPatch'' "inserted thing" ] +-- x = DiffSummaryForBranch'' "module Foo" [ DiffSummaryForBranch'' "module Bar" [ DiffSummaryForPatch'' "deleted thing" ], DiffSummaryForPatch'' "inserted thing" ] + +-- data Context = DiffContext DiffSummary +-- deriving (Show) + +-- data DiffResult = DiffResult { +-- assumptions :: M.Map String [DiffSummary] +-- } deriving (Show) + +-- instance Monoid DiffResult where +-- mempty = DiffResult mempty +-- mappend a b = DiffResult (assumptions a `mappend` assumptions b) + +-- newtype DiffState t m = DiffState { +-- memo :: M.Map t m +-- } + +-- type Summarize t = State (DiffState t (DiffSummary, DiffResult)) (DiffSummary, DiffResult) + +-- memoSummarize :: Ord c => (c -> Summarize c) -> c -> Summarize c +-- memoSummarize f c = gets memo >>= maybe memoize return . M.lookup c where +-- memoize = do +-- r <- f c +-- modify $ \s -> s { memo = M.insert c r $ memo s } +-- return r + +-- generateSummaries :: DiffF leaf Info f -> Summarize (Diff leaf Info) +-- generateSummaries (Pure patch) = return (DEmptySummary, mempty) +-- generateSummaries (Free (_ :< Leaf _)) = return (DEmptySummary, mempty) +-- generateSummaries (Free (info :< Indexed f)) = do +-- childStates <- _ +-- -- * -- +-- -- * -- +-- -- * + + -- let childContexts = maybe [] (map $ DiffContext _) (M.lookup key . assumptions $ snd childState) + -- let as = M.delete key . assumptions $ snd childState + -- let diffResult = DiffResult { + -- assumptions = _ } + -- return (toCategory (snd info), diffResult) -- T { 1 } -- T { 2 } -- T {1, 2} @@ -34,57 +104,61 @@ data DiffSummary = DiffSummary { beforeSummary :: DiffSummary, afterSummary :: D -- TermSummary { name1 = Just "1", name2 :: Just "2" } -- DiffSummary { beforeSummary = (TermSummary { name1 = Just "1"}), afterSummary = (TermSummary { name = Just "2"}), diffContext = Nothing } -instance (Monoid DiffSummary) where - mempty = EmptySummary - mappend EmptySummary EmptySummary = EmptySummary - mappend EmptySummary summary@TermSummary{..} = summary - mappend s@TermSummary{} EmptySummary = s - mappend EmptySummary summary@DiffSummary{..} = summary - mappend s1@TermSummary{} s2@TermSummary{} = DiffSummary { beforeSummary = s1, afterSummary = s2, context = Nothing } - mappend s@TermSummary{} DiffSummary{..} = DiffSummary { beforeSummary = mappend s beforeSummary, afterSummary = afterSummary, context = context } - mappend DiffSummary{..} s@TermSummary{} = DiffSummary { beforeSummary = beforeSummary, afterSummary = mappend afterSummary s, context = context } - mappend summary@DiffSummary{} EmptySummary = summary - mappend s1@DiffSummary{} s2@DiffSummary{} = DiffSummary { beforeSummary = mappend (beforeSummary s1) (beforeSummary s2), afterSummary = mappend (afterSummary s1) (afterSummary s2), context = mappend (context s1) (context s2) } +info :: Info +info = Info (R.rangeAt 0) mempty 1 -emptyDiffSummary :: DiffSummary -emptyDiffSummary = EmptySummary +eLeaf :: Diff String Info +eLeaf = retract . free . Pure . Insert . cofree $ info :< Leaf "a" -patchToSummary :: (Term a Info -> DiffSummary) -> Patch (Term a Info) -> DiffSummary -patchToSummary termSummary patch = memptyOrDiff (before patch) <> memptyOrDiff (after patch) - where - memptyOrDiff = maybe emptyDiffSummary termSummary +freeLeaf :: Diff String Info +freeLeaf = free . Free $ (pure info :< Indexed [free . Free $ (pure info :< Leaf "a"), free $ Pure (Insert $ cofree (info :< Leaf "b"))]) -type DiffSummaryF leaf annotation = FreeF (CofreeF (Syntax leaf) (Both annotation)) +eIndexed :: Diff String Info +eIndexed = free . Pure . Insert . cofree $ info :< Indexed [cofree $ info :< Leaf "a"] -diffSummary :: Diff leaf Info -> DiffSummary --- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a -diffSummary = histo diffSummary' . fmap (patchToSummary termToSummary) where - --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary - -- Skip any child that doesn't have any changes (that will always include leaves) - diffSummary' :: DiffSummaryF leaf Info DiffSummary (Cofree.Cofree (DiffSummaryF leaf Info DiffSummary) DiffSummary) -> DiffSummary - diffSummary' (Free (info :< Leaf _)) = toCategory (fst info) <> toCategory (snd info) - diffSummary' (Free (_ :< Indexed children)) = mconcat $ extract <$> children - diffSummary' (Free (_ :< Fixed children)) = mconcat $ extract <$> children - diffSummary' (Free (_ :< Keyed children)) = undefined - diffSummary' (Pure diffSummary) = diffSummary :: DiffSummary - -- (patchSummary termSummary) +patchToSummary :: (Term a Info -> DiffSummary a) -> Patch (Term a Info) -> DiffSummary a +patchToSummary termSummary patch = undefined -- memptyOrDiff (before patch) <> memptyOrDiff (after patch) + +-- diffSummary :: Diff leaf Info -> DiffSummary a +-- -- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a +-- diffSummary = histo diffSummary' . fmap (patchToSummary termToSummary) where +-- --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary +-- -- Skip any child that doesn't have any changes (that will always include leaves) +-- diffSummary' :: DiffSummaryF leaf Info DiffSummary (Cofree.Cofree (DiffSummaryF leaf Info DiffSummary) DiffSummary) -> DiffSummary +-- -- Prune leaves +-- diffSummary' (Free (info :< Leaf _)) = undefined +-- -- Return a contextless indexed summary with it's indexed context distributed to its children +-- diffSummary' (Free (_ :< Indexed [])) = undefined +-- diffSummary' (Free (_ :< Indexed ((summary Cofree.:< f):xs))) = summary :: DiffSummary +-- -- Return a contextless fixed diff summary with it's fixed context distributed to its children +-- diffSummary' (Free (_ :< Fixed children)) = undefined +-- diffSummary' (Free (_ :< Keyed children)) = undefined +-- -- Return a contextless diff summary +-- diffSummary' (Pure summary) = summary :: DiffSummary +-- -- (patchSummary termSummary) + +diffSummary' :: Diff leaf Info -> DiffSummary a +-- futu :: Unfoldable t => (a -> Base t (Free (Base t) a)) -> a -> t +diffSummary' = futu diffSummary'' where + diffSummary'' :: (Diff leaf Info) -> Prim (DiffSummary a) (Free.Free (Prim (DiffSummary a)) (Diff leaf Info)) + diffSummary'' diff = case project diff of -- Syntax Text DiffSummary -> DiffSummary Text -- If termSummary returns a DiffEntry that just contains the term name, we need to -- Instead of foldMap we need a histomorphism -termToSummary :: Term leaf Info -> DiffSummary +termToSummary :: Term leaf Info -> DiffSummary a termToSummary = Foldable.cata summary where - summary :: TermF leaf Info f -> DiffSummary - summary (info :< Leaf replace) = toCategory info - summary (info :< Indexed children) = toCategory info - summary (info :< Fixed children) = toCategory info - summary (info :< Keyed _) = toCategory info + summary :: TermF leaf Info f -> DiffSummary a + summary (info :< Leaf replace) = toCategory info replace + summary (info :< Indexed children) = _ + summary (info :< Fixed children) = _ + summary (info :< Keyed _) = _ maybeFirstCategory :: (Categorizable a) => a -> Maybe Category maybeFirstCategory term = listToMaybe . toList $ Category.categories term -toCategory :: Info -> DiffSummary -toCategory info = case maybeFirstCategory info of - Just category -> TermSummary { termName = show category } - Nothing -> EmptySummary +toCategory :: Info -> a -> DiffSummary a +toCategory info a = case maybeFirstCategory info of + Just category -> DTermSummary (show category) a + Nothing -> DEmptySummary From ec58e29855a74d47989fee572a3a9c454a82b49b Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 10:54:05 -0400 Subject: [PATCH 19/53] diff summaries kind of work --- src/DiffSummary.hs | 101 +++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 6762cf0e4..da70f032b 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -19,34 +19,25 @@ import Control.Monad.Trans.Free import qualified Control.Monad.Free as Free import qualified Control.Comonad.Cofree as Cofree import Data.Functor.Foldable as Foldable +import qualified Data.Foldable as F import Control.Monad.State hiding (sequence) import qualified Data.Map as M import Data.Functor.Identity -instance Unfoldable (DiffSummary a) where - embed (DiffSummary x y) = (DDiffSummary x y) - embed (TermSummary s b f) = DTermSummary s b - embed EmptySummary = DEmptySummary - - apo f a = case f a of - Cons x (Left xs) -> x : xs - Cons x (Right b) -> x : apo f b - Nil -> [] - - --- * -- --- -- * - -data DiffSummary a = BranchSummary [DiffSummary a] - | TermSummary String a - | EmptySummary +data DiffSummary a = TermSummary { + description :: String, + annotation :: a, + parentAnnotations :: [a] +} deriving (Eq, Show, Functor, Ord) -data instance Prim (DiffSummary a) b = PBranchSummary (Prim [a] b) b | PTermSummary String a b | PEmptySummary +data instance Prim (DiffSummary a) b = PBranchSummary a b | PTermSummary String a b | PParentSummary a + deriving (Show, Functor) type instance Base (DiffSummary a) = Prim (DiffSummary a) -instance Foldable.Foldable (DiffSummary a) where project = Const -instance Unfoldable (DiffSummary a) where embed = getConst +-- instance Unfoldable (DiffSummary a) where +-- embed (PTermSummary s a b) = TermSummary s a b +-- embed (PParentSummary a) = ParentSummary a -- data DiffSummary' = [(String, [String])] @@ -118,31 +109,43 @@ eIndexed = free . Pure . Insert . cofree $ info :< Indexed [cofree $ info :< Lea patchToSummary :: (Term a Info -> DiffSummary a) -> Patch (Term a Info) -> DiffSummary a patchToSummary termSummary patch = undefined -- memptyOrDiff (before patch) <> memptyOrDiff (after patch) - --- diffSummary :: Diff leaf Info -> DiffSummary a --- -- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a --- diffSummary = histo diffSummary' . fmap (patchToSummary termToSummary) where --- --diffSummary' :: DiffF leaf (Cofree.Cofree (DiffF leaf Info) DiffSummary) f -> DiffSummary --- -- Skip any child that doesn't have any changes (that will always include leaves) --- diffSummary' :: DiffSummaryF leaf Info DiffSummary (Cofree.Cofree (DiffSummaryF leaf Info DiffSummary) DiffSummary) -> DiffSummary --- -- Prune leaves --- diffSummary' (Free (info :< Leaf _)) = undefined --- -- Return a contextless indexed summary with it's indexed context distributed to its children --- diffSummary' (Free (_ :< Indexed [])) = undefined --- diffSummary' (Free (_ :< Indexed ((summary Cofree.:< f):xs))) = summary :: DiffSummary --- -- Return a contextless fixed diff summary with it's fixed context distributed to its children --- diffSummary' (Free (_ :< Fixed children)) = undefined --- diffSummary' (Free (_ :< Keyed children)) = undefined --- -- Return a contextless diff summary --- diffSummary' (Pure summary) = summary :: DiffSummary --- -- (patchSummary termSummary) -diffSummary' :: Diff leaf Info -> DiffSummary a --- futu :: Unfoldable t => (a -> Base t (Free (Base t) a)) -> a -> t -diffSummary' = futu diffSummary'' where - diffSummary'' :: (Diff leaf Info) -> Prim (DiffSummary a) (Free.Free (Prim (DiffSummary a)) (Diff leaf Info)) - diffSummary'' diff = case project diff of +diffSummary :: Diff leaf Info -> [DiffSummary ()] +-- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a +diffSummary = cata diffSummary' where + diffSummary' :: DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] + -- Skip any child that doesn't have any changes (that will always include leaves) + -- Prune leaves + diffSummary' (Free (info :< Leaf _)) = [] + -- Return a contextless indexed summary with it's indexed context distributed to its children + diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children + -- Return a contextless fixed diff summary with it's fixed context distributed to its children + diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children + diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) + -- Return a contextless diff summary + diffSummary' (Pure (Insert term)) = [TermSummary "insert" () []] + diffSummary' (Pure (Delete term)) = [TermSummary "delete" () []] + diffSummary' (Pure (Replace t1 t2)) = [TermSummary "replace" () []] +prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } + -- (patchSummary termSummary) + +-- diffSummary'' :: Diff leaf Info -> DiffSummary () +-- diffSummary'' = hylo combineSummaries tearDiffs where +-- combineSummaries :: FreeF (Syntax leaf) (Patch (Term leaf Info)) (DiffSummary ()) -> DiffSummary () +-- combineSummaries (Pure (Insert term)) = TermSummary "insert" () +-- tearDiffs :: Diff leaf Info -> FreeF (Syntax leaf) (Patch (Term leaf Info)) (Diff leaf Info) +-- tearDiffs = undefined + +-- diffSummary' :: Diff leaf Info -> DiffSummary () +-- -- futu :: Unfoldable t => (a -> Base t (Free (Base t) a)) -> a -> t +-- diffSummary' = futu diffSummary'' where +-- diffSummary'' :: Diff leaf Info -> Prim (DiffSummary a) (Free.Free (Prim (DiffSummary a)) (Diff leaf Info)) +-- diffSummary'' diff = case project diff of +-- Pure (Insert term) -> PTermSummary "insert" () undefined +-- Pure (Delete term) -> undefined +-- Pure (Replace t1 t2) -> undefined +-- Free (ann :< Leaf a) -> undefined -- Syntax Text DiffSummary -> DiffSummary Text -- If termSummary returns a DiffEntry that just contains the term name, we need to -- Instead of foldMap we need a histomorphism @@ -150,15 +153,15 @@ diffSummary' = futu diffSummary'' where termToSummary :: Term leaf Info -> DiffSummary a termToSummary = Foldable.cata summary where summary :: TermF leaf Info f -> DiffSummary a - summary (info :< Leaf replace) = toCategory info replace - summary (info :< Indexed children) = _ - summary (info :< Fixed children) = _ - summary (info :< Keyed _) = _ + summary (info :< Leaf replace) = undefined + summary (info :< Indexed children) = undefined + summary (info :< Fixed children) = undefined + summary (info :< Keyed _) = undefined maybeFirstCategory :: (Categorizable a) => a -> Maybe Category maybeFirstCategory term = listToMaybe . toList $ Category.categories term toCategory :: Info -> a -> DiffSummary a toCategory info a = case maybeFirstCategory info of - Just category -> DTermSummary (show category) a - Nothing -> DEmptySummary + Just category -> undefined + Nothing -> undefined From 95cb5070fabda1f045751ec61a28e83e16be2dfb Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 11:15:47 -0400 Subject: [PATCH 20/53] :fire: failed diff summary funs --- src/DiffSummary.hs | 103 +-------------------------------------------- 1 file changed, 1 insertion(+), 102 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index da70f032b..9d73bf5e2 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -35,120 +35,19 @@ data instance Prim (DiffSummary a) b = PBranchSummary a b | PTermSummary String deriving (Show, Functor) type instance Base (DiffSummary a) = Prim (DiffSummary a) --- instance Unfoldable (DiffSummary a) where --- embed (PTermSummary s a b) = TermSummary s a b --- embed (PParentSummary a) = ParentSummary a - --- data DiffSummary' = [(String, [String])] - --- data DiffSummary'' = DiffSummaryForPatch'' String | DiffSummaryForBranch'' String [DiffSummary''] - --- x = DiffSummaryForBranch'' "module Foo" [ DiffSummaryForPatch'' "inserted thing" ] --- x = DiffSummaryForBranch'' "module Foo" [ DiffSummaryForBranch'' "module Bar" [ DiffSummaryForPatch'' "deleted thing" ], DiffSummaryForPatch'' "inserted thing" ] - --- data Context = DiffContext DiffSummary --- deriving (Show) - --- data DiffResult = DiffResult { --- assumptions :: M.Map String [DiffSummary] --- } deriving (Show) - --- instance Monoid DiffResult where --- mempty = DiffResult mempty --- mappend a b = DiffResult (assumptions a `mappend` assumptions b) - --- newtype DiffState t m = DiffState { --- memo :: M.Map t m --- } - --- type Summarize t = State (DiffState t (DiffSummary, DiffResult)) (DiffSummary, DiffResult) - --- memoSummarize :: Ord c => (c -> Summarize c) -> c -> Summarize c --- memoSummarize f c = gets memo >>= maybe memoize return . M.lookup c where --- memoize = do --- r <- f c --- modify $ \s -> s { memo = M.insert c r $ memo s } --- return r - --- generateSummaries :: DiffF leaf Info f -> Summarize (Diff leaf Info) --- generateSummaries (Pure patch) = return (DEmptySummary, mempty) --- generateSummaries (Free (_ :< Leaf _)) = return (DEmptySummary, mempty) --- generateSummaries (Free (info :< Indexed f)) = do --- childStates <- _ --- -- * -- --- -- * -- --- -- * - - -- let childContexts = maybe [] (map $ DiffContext _) (M.lookup key . assumptions $ snd childState) - -- let as = M.delete key . assumptions $ snd childState - -- let diffResult = DiffResult { - -- assumptions = _ } - -- return (toCategory (snd info), diffResult) - -- T { 1 } - -- T { 2 } - -- T {1, 2} - -- T {'array', 'dictionary'} - -- T {Nothing, 'dictionary'} - - -- Given two (Both (Maybe String)) - -- TermSummary { name1 :: Maybe String, name2 :: Maybe String } - -- TermSummary { name1 = Just "1", name2 :: Just "2" } - -- DiffSummary { beforeSummary = (TermSummary { name1 = Just "1"}), afterSummary = (TermSummary { name = Just "2"}), diffContext = Nothing } - -info :: Info -info = Info (R.rangeAt 0) mempty 1 - -eLeaf :: Diff String Info -eLeaf = retract . free . Pure . Insert . cofree $ info :< Leaf "a" - -freeLeaf :: Diff String Info -freeLeaf = free . Free $ (pure info :< Indexed [free . Free $ (pure info :< Leaf "a"), free $ Pure (Insert $ cofree (info :< Leaf "b"))]) - -eIndexed :: Diff String Info -eIndexed = free . Pure . Insert . cofree $ info :< Indexed [cofree $ info :< Leaf "a"] - -patchToSummary :: (Term a Info -> DiffSummary a) -> Patch (Term a Info) -> DiffSummary a -patchToSummary termSummary patch = undefined -- memptyOrDiff (before patch) <> memptyOrDiff (after patch) diffSummary :: Diff leaf Info -> [DiffSummary ()] --- histo :: Foldable t => (Base t (Cofree (Base t) a) -> a) -> t -> a diffSummary = cata diffSummary' where diffSummary' :: DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] - -- Skip any child that doesn't have any changes (that will always include leaves) - -- Prune leaves - diffSummary' (Free (info :< Leaf _)) = [] - -- Return a contextless indexed summary with it's indexed context distributed to its children + diffSummary' (Free (info :< Leaf _)) = [] -- Skip leaves since they don't have any changes diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children - -- Return a contextless fixed diff summary with it's fixed context distributed to its children diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) - -- Return a contextless diff summary diffSummary' (Pure (Insert term)) = [TermSummary "insert" () []] diffSummary' (Pure (Delete term)) = [TermSummary "delete" () []] diffSummary' (Pure (Replace t1 t2)) = [TermSummary "replace" () []] prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } - -- (patchSummary termSummary) - --- diffSummary'' :: Diff leaf Info -> DiffSummary () --- diffSummary'' = hylo combineSummaries tearDiffs where --- combineSummaries :: FreeF (Syntax leaf) (Patch (Term leaf Info)) (DiffSummary ()) -> DiffSummary () --- combineSummaries (Pure (Insert term)) = TermSummary "insert" () --- tearDiffs :: Diff leaf Info -> FreeF (Syntax leaf) (Patch (Term leaf Info)) (Diff leaf Info) --- tearDiffs = undefined - --- diffSummary' :: Diff leaf Info -> DiffSummary () --- -- futu :: Unfoldable t => (a -> Base t (Free (Base t) a)) -> a -> t --- diffSummary' = futu diffSummary'' where --- diffSummary'' :: Diff leaf Info -> Prim (DiffSummary a) (Free.Free (Prim (DiffSummary a)) (Diff leaf Info)) --- diffSummary'' diff = case project diff of --- Pure (Insert term) -> PTermSummary "insert" () undefined --- Pure (Delete term) -> undefined --- Pure (Replace t1 t2) -> undefined --- Free (ann :< Leaf a) -> undefined --- Syntax Text DiffSummary -> DiffSummary Text --- If termSummary returns a DiffEntry that just contains the term name, we need to --- Instead of foldMap we need a histomorphism termToSummary :: Term leaf Info -> DiffSummary a termToSummary = Foldable.cata summary where From e6a385e09a4d927d995885eb9fcd679a5362f6f8 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 11:23:32 -0400 Subject: [PATCH 21/53] add type signature --- src/DiffSummary.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 9d73bf5e2..79ca332dc 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -47,6 +47,7 @@ diffSummary = cata diffSummary' where diffSummary' (Pure (Delete term)) = [TermSummary "delete" () []] diffSummary' (Pure (Replace t1 t2)) = [TermSummary "replace" () []] +prependSummary :: a -> DiffSummary a -> DiffSummary a prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } termToSummary :: Term leaf Info -> DiffSummary a From e53333c9dd5bee67101363b82afa6e43dc19a10c Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 11:23:57 -0400 Subject: [PATCH 22/53] indentation --- src/DiffSummary.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 79ca332dc..396c58724 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -28,8 +28,7 @@ data DiffSummary a = TermSummary { description :: String, annotation :: a, parentAnnotations :: [a] -} - deriving (Eq, Show, Functor, Ord) +} deriving (Eq, Show, Functor, Ord) data instance Prim (DiffSummary a) b = PBranchSummary a b | PTermSummary String a b | PParentSummary a deriving (Show, Functor) From b16166d78a8362270c8dbcd6c146c108e6e9e020 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 13:19:30 -0400 Subject: [PATCH 23/53] clean up imports --- src/DiffSummary.hs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 396c58724..961385e73 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving, DataKinds, RecordWildCards, TypeFamilies #-} +{-# LANGUAGE DataKinds, TypeFamilies #-} module DiffSummary where import Prelude hiding (fst, snd) @@ -7,22 +7,14 @@ import Info import Patch import Term import Syntax -import qualified Range as R -import Category as Category -import Data.Functor.Both -import Data.Monoid +import Category import Data.Maybe (listToMaybe) import Data.Set (toList) -import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free -import qualified Control.Monad.Free as Free -import qualified Control.Comonad.Cofree as Cofree +import Control.Monad import Data.Functor.Foldable as Foldable import qualified Data.Foldable as F -import Control.Monad.State hiding (sequence) -import qualified Data.Map as M -import Data.Functor.Identity data DiffSummary a = TermSummary { description :: String, From 727b1538c3730841615902d80483d1a79dfbd38d Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 14:27:44 -0400 Subject: [PATCH 24/53] Remove termToSummary --- src/DiffSummary.hs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 961385e73..ba26930fe 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -41,14 +41,6 @@ diffSummary = cata diffSummary' where prependSummary :: a -> DiffSummary a -> DiffSummary a prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } -termToSummary :: Term leaf Info -> DiffSummary a -termToSummary = Foldable.cata summary where - summary :: TermF leaf Info f -> DiffSummary a - summary (info :< Leaf replace) = undefined - summary (info :< Indexed children) = undefined - summary (info :< Fixed children) = undefined - summary (info :< Keyed _) = undefined - maybeFirstCategory :: (Categorizable a) => a -> Maybe Category maybeFirstCategory term = listToMaybe . toList $ Category.categories term From 45a2b70a690f411d5d0c20dc653c9d8f95771f05 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 14:29:36 -0400 Subject: [PATCH 25/53] Remove Term import --- src/DiffSummary.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index ba26930fe..aeca9a396 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -5,7 +5,6 @@ import Prelude hiding (fst, snd) import Diff import Info import Patch -import Term import Syntax import Category import Data.Maybe (listToMaybe) @@ -30,7 +29,7 @@ type instance Base (DiffSummary a) = Prim (DiffSummary a) diffSummary :: Diff leaf Info -> [DiffSummary ()] diffSummary = cata diffSummary' where diffSummary' :: DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] - diffSummary' (Free (info :< Leaf _)) = [] -- Skip leaves since they don't have any changes + diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) From 927acafcbd48a945614959d2b7477604cbd998e4 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 16 May 2016 14:43:53 -0400 Subject: [PATCH 26/53] add DiffSummarySpec --- semantic-diff.cabal | 1 + test/DiffSummarySpec.hs | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 test/DiffSummarySpec.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index cca89e4d6..bd3566c25 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -79,6 +79,7 @@ test-suite semantic-diff-test , OrderedMapSpec , PatchOutputSpec , TermSpec + , DiffSummarySpec build-depends: base , bytestring , containers diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs new file mode 100644 index 000000000..e5e0e7ebd --- /dev/null +++ b/test/DiffSummarySpec.hs @@ -0,0 +1,5 @@ +module AlignmentSpec where + +import Test.Hspec + +import DiffSummary From 4d6427a24e6a3d705757fa225b7bc42c2d0ad460 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 17 May 2016 13:09:14 -0400 Subject: [PATCH 27/53] Stub some tests --- src/DiffSummary.hs | 27 +++++++++++++++------------ test/DiffSummarySpec.hs | 12 +++++++++++- test/Spec.hs | 2 ++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index aeca9a396..fe11962d1 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,5 +1,5 @@ -{-# LANGUAGE DataKinds, TypeFamilies #-} -module DiffSummary where +{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-} +module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where import Prelude hiding (fst, snd) import Diff @@ -15,16 +15,19 @@ import Control.Monad import Data.Functor.Foldable as Foldable import qualified Data.Foldable as F -data DiffSummary a = TermSummary { +data DiffInfo = DiffInfo deriving (Eq) + +data DiffSummary a = DiffSummary { description :: String, - annotation :: a, + patch :: Patch DiffInfo, parentAnnotations :: [a] -} deriving (Eq, Show, Functor, Ord) +} deriving (Eq, Functor) -data instance Prim (DiffSummary a) b = PBranchSummary a b | PTermSummary String a b | PParentSummary a - deriving (Show, Functor) - -type instance Base (DiffSummary a) = Prim (DiffSummary a) +instance Show a => Show (DiffSummary a) where + show diffSummary = case patch diffSummary of + (Replace _ _) -> "Replaced " + (Insert term) -> "Added " + (Delete term) -> "Deleted " diffSummary :: Diff leaf Info -> [DiffSummary ()] diffSummary = cata diffSummary' where @@ -33,9 +36,9 @@ diffSummary = cata diffSummary' where diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) - diffSummary' (Pure (Insert term)) = [TermSummary "insert" () []] - diffSummary' (Pure (Delete term)) = [TermSummary "delete" () []] - diffSummary' (Pure (Replace t1 t2)) = [TermSummary "replace" () []] + diffSummary' (Pure (Insert _)) = [DiffSummary "insert" (Insert DiffInfo) []] + diffSummary' (Pure (Delete _)) = [DiffSummary "delete" (Delete DiffInfo) []] + diffSummary' (Pure (Replace _ _)) = [DiffSummary "delete" (Replace DiffInfo DiffInfo) []] prependSummary :: a -> DiffSummary a -> DiffSummary a prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index e5e0e7ebd..b4e058f46 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -1,5 +1,15 @@ -module AlignmentSpec where +module DiffSummarySpec where import Test.Hspec +import Patch import DiffSummary + +testSummary :: DiffSummary String +testSummary = DiffSummary { description = "lol", patch = Insert DiffInfo, parentAnnotations = [] } + +spec :: Spec +spec = parallel $ do + describe "show" $ do + it "should print adds" $ + show testSummary `shouldBe` "Added an 'a' expression" diff --git a/test/Spec.hs b/test/Spec.hs index 0f6609a7a..083588151 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -8,6 +8,7 @@ import qualified InterpreterSpec import qualified OrderedMapSpec import qualified PatchOutputSpec import qualified TermSpec +import qualified DiffSummarySpec import Test.Hspec main :: IO () @@ -20,3 +21,4 @@ main = hspec $ parallel $ do describe "OrderedMap" OrderedMapSpec.spec describe "PatchOutput" PatchOutputSpec.spec describe "Term" TermSpec.spec + describe "DiffSummary" DiffSummarySpec.spec From 68f5b0f669edce89494ea65fd6334613833d851a Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 17 May 2016 15:59:07 -0400 Subject: [PATCH 28/53] Remove description from diff summary --- src/DiffSummary.hs | 34 ++++++++++++++++++---------------- src/Info.hs | 4 ++++ test/DiffSummarySpec.hs | 25 ++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index fe11962d1..dfa2a9122 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -5,9 +5,10 @@ import Prelude hiding (fst, snd) import Diff import Info import Patch +import Term import Syntax import Category -import Data.Maybe (listToMaybe) +import Data.Maybe (fromMaybe) import Data.Set (toList) import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free @@ -15,10 +16,9 @@ import Control.Monad import Data.Functor.Foldable as Foldable import qualified Data.Foldable as F -data DiffInfo = DiffInfo deriving (Eq) +data DiffInfo = DiffInfo { termName :: String } deriving (Eq) data DiffSummary a = DiffSummary { - description :: String, patch :: Patch DiffInfo, parentAnnotations :: [a] } deriving (Eq, Functor) @@ -26,27 +26,29 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show diffSummary = case patch diffSummary of (Replace _ _) -> "Replaced " - (Insert term) -> "Added " - (Delete term) -> "Deleted " + (Insert termInfo) -> "Added " ++ show (termName termInfo) + (Delete termInfo) -> "Deleted " -diffSummary :: Diff leaf Info -> [DiffSummary ()] +diffSummary :: Show leaf => Diff leaf Info -> [DiffSummary ()] diffSummary = cata diffSummary' where - diffSummary' :: DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] + diffSummary' :: Show leaf => DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) - diffSummary' (Pure (Insert _)) = [DiffSummary "insert" (Insert DiffInfo) []] - diffSummary' (Pure (Delete _)) = [DiffSummary "delete" (Delete DiffInfo) []] - diffSummary' (Pure (Replace _ _)) = [DiffSummary "delete" (Replace DiffInfo DiffInfo) []] + diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toTermName term))) []] + diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toTermName term))) []] + diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toTermName t1)) (DiffInfo (toTermName t2))) []] prependSummary :: a -> DiffSummary a -> DiffSummary a prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } -maybeFirstCategory :: (Categorizable a) => a -> Maybe Category -maybeFirstCategory term = listToMaybe . toList $ Category.categories term +toTermName :: Show leaf => Term leaf Info -> String +toTermName term = case runCofree term of + (_ :< (Leaf leaf)) -> show leaf + (info :< Indexed _) -> show $ toCategory info + (info :< Fixed _) -> show $ toCategory info + (info :< Keyed _) -> show $ toCategory info -toCategory :: Info -> a -> DiffSummary a -toCategory info a = case maybeFirstCategory info of - Just category -> undefined - Nothing -> undefined +toCategory :: Info -> Category +toCategory info = fromMaybe (Other "Unknown") (maybeFirstCategory info) diff --git a/src/Info.hs b/src/Info.hs index 98e16ce86..24ca94a6e 100644 --- a/src/Info.hs +++ b/src/Info.hs @@ -1,5 +1,6 @@ module Info where +import Data.Maybe (listToMaybe) import Category import Data.Set import Range @@ -11,3 +12,6 @@ data Info = Info { characterRange :: !Range, categories :: !(Set Category), size instance Categorizable Info where categories = Info.categories + +maybeFirstCategory :: (Categorizable a) => a -> Maybe Category +maybeFirstCategory term = listToMaybe . toList $ Category.categories term diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index b4e058f46..09062aede 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -1,15 +1,34 @@ module DiffSummarySpec where import Test.Hspec - +import Diff +import Info +import Syntax +import Control.Comonad.Trans.Cofree +import Control.Monad.Trans.Free import Patch +import Range +import Category +import Data.Set import DiffSummary -testSummary :: DiffSummary String -testSummary = DiffSummary { description = "lol", patch = Insert DiffInfo, parentAnnotations = [] } +arrayInfo :: Info +arrayInfo = Info (rangeAt 0) (singleton ArrayLiteral) 2 + +literalInfo :: Info +literalInfo = Info (rangeAt 1) (singleton StringLiteral) 1 + +testDiff :: Diff String Info +testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) + +testSummary :: DiffSummary Char +testSummary = DiffSummary { patch = Insert (DiffInfo "a"), parentAnnotations = [] } spec :: Spec spec = parallel $ do + describe "diffSummary" $ do + it "outputs a diff summary" $ do + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "a"), parentAnnotations = [()] } ] describe "show" $ do it "should print adds" $ show testSummary `shouldBe` "Added an 'a' expression" From 5a8aed6e511f8c5a6be49716f2ef7d47543d251b Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 17 May 2016 17:34:27 -0400 Subject: [PATCH 29/53] map infos to DifInfos in diffSummary' --- semantic-diff.cabal | 1 + src/DiffSummary.hs | 55 ++++++++++++++++++++++++++++++----------- test/DiffSummarySpec.hs | 4 +-- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index bd3566c25..b7db4b044 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -62,6 +62,7 @@ library , recursion-schemes , free , comonad + , split default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, OverloadedStrings ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index dfa2a9122..40b991d76 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-} +{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables, FlexibleInstances #-} module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where import Prelude hiding (fst, snd) @@ -10,45 +10,70 @@ import Syntax import Category import Data.Maybe (fromMaybe) import Data.Set (toList) +import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import Control.Monad +import Data.List import Data.Functor.Foldable as Foldable +import Data.Functor.Both import qualified Data.Foldable as F -data DiffInfo = DiffInfo { termName :: String } deriving (Eq) +data DiffInfo = DiffInfo { name :: String } deriving (Eq, Show) + +class IsTerm a where + termName :: a -> String + +instance IsTerm DiffInfo where + termName = name + +instance IsTerm String where + termName = id + +instance IsTerm Category where + termName category = case category of + BinaryOperator -> "binary operator" + DictionaryLiteral -> "dictionary literal" + Pair -> "pair" + FunctionCall -> "function call" + StringLiteral -> "string literal" + IntegerLiteral -> "integer literal" + SymbolLiteral -> "symbol literal" + ArrayLiteral -> "array literal" + (Other s) -> s data DiffSummary a = DiffSummary { patch :: Patch DiffInfo, - parentAnnotations :: [a] + parentAnnotations :: [DiffInfo] } deriving (Eq, Functor) instance Show a => Show (DiffSummary a) where show diffSummary = case patch diffSummary of (Replace _ _) -> "Replaced " - (Insert termInfo) -> "Added " ++ show (termName termInfo) + (Insert termInfo) -> "Added the " ++ "'" ++ termName termInfo ++ "' " + ++ "to the " ++ intercalate "#" (termName <$> parentAnnotations diffSummary) ++ " context" (Delete termInfo) -> "Deleted " -diffSummary :: Show leaf => Diff leaf Info -> [DiffSummary ()] +diffSummary :: Show leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where - diffSummary' :: Show leaf => DiffF leaf Info [DiffSummary ()] -> [DiffSummary ()] + diffSummary' :: Show leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes - diffSummary' (Free (_ :< Indexed children)) = prependSummary () <$> join children - diffSummary' (Free (_ :< Fixed children)) = prependSummary () <$> join children - diffSummary' (Free (_ :< Keyed children)) = prependSummary () <$> join (F.toList children) + diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join children + diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join children + diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join (F.toList children) diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toTermName term))) []] diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toTermName term))) []] diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toTermName t1)) (DiffInfo (toTermName t2))) []] -prependSummary :: a -> DiffSummary a -> DiffSummary a +prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } toTermName :: Show leaf => Term leaf Info -> String -toTermName term = case runCofree term of - (_ :< (Leaf leaf)) -> show leaf - (info :< Indexed _) -> show $ toCategory info - (info :< Fixed _) -> show $ toCategory info - (info :< Keyed _) -> show $ toCategory info +toTermName term = termName $ case runCofree term of + (info :< Leaf _) -> toCategory info + (info :< Indexed _) -> toCategory info + (info :< Fixed _) -> toCategory info + (info :< Keyed _) -> toCategory info toCategory :: Info -> Category toCategory info = fromMaybe (Other "Unknown") (maybeFirstCategory info) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 09062aede..ce22c6efb 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -22,13 +22,13 @@ testDiff :: Diff String Info testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) testSummary :: DiffSummary Char -testSummary = DiffSummary { patch = Insert (DiffInfo "a"), parentAnnotations = [] } +testSummary = DiffSummary { patch = Insert (DiffInfo "string literal"), parentAnnotations = [] } spec :: Spec spec = parallel $ do describe "diffSummary" $ do it "outputs a diff summary" $ do - diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "a"), parentAnnotations = [()] } ] + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string literal"), parentAnnotations = [ DiffInfo "array literal" ] } ] describe "show" $ do it "should print adds" $ show testSummary `shouldBe` "Added an 'a' expression" From eedea216b3871081d9271b66af6d1f3047cdd6e1 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 09:55:59 -0400 Subject: [PATCH 30/53] add delete case --- src/DiffSummary.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 40b991d76..d211cf67c 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -49,10 +49,11 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show diffSummary = case patch diffSummary of - (Replace _ _) -> "Replaced " - (Insert termInfo) -> "Added the " ++ "'" ++ termName termInfo ++ "' " + (Insert termInfo) -> "Added " ++ "'" ++ termName termInfo ++ "' " ++ "to the " ++ intercalate "#" (termName <$> parentAnnotations diffSummary) ++ " context" - (Delete termInfo) -> "Deleted " + (Delete termInfo) -> "Deleted " ++ "'" ++ termName termInfo ++ "' " + ++ "in the " ++ intercalate "#" (termName <$> parentAnnotations diffSummary) ++ " context" + (Replace t1 t2) -> "Replaced " diffSummary :: Show leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where From ab3b8faf329092127a8085e49db620bfe35f2527 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 10:18:26 -0400 Subject: [PATCH 31/53] hey look it works --- src/DiffSummary.hs | 42 +++++++++++++++++++++++++---------------- test/DiffSummarySpec.hs | 6 +++--- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index d211cf67c..260968770 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables, FlexibleInstances #-} +{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables, FlexibleInstances, RecordWildCards #-} module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where import Prelude hiding (fst, snd) @@ -14,12 +14,22 @@ import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import Control.Monad +import Data.Maybe import Data.List import Data.Functor.Foldable as Foldable import Data.Functor.Both import qualified Data.Foldable as F -data DiffInfo = DiffInfo { name :: String } deriving (Eq, Show) +data DiffInfo = DiffInfo { name :: String, term :: Maybe String } deriving (Eq, Show) + +class ToTerm a where + toTerm :: a -> Maybe String + + +instance IsTerm leaf => ToTerm (Term leaf Info) where + toTerm term = case runCofree term of + (_ :< Leaf leaf) -> Just (termName leaf) + _ -> Nothing class IsTerm a where termName :: a -> String @@ -48,28 +58,28 @@ data DiffSummary a = DiffSummary { } deriving (Eq, Functor) instance Show a => Show (DiffSummary a) where - show diffSummary = case patch diffSummary of - (Insert termInfo) -> "Added " ++ "'" ++ termName termInfo ++ "' " - ++ "to the " ++ intercalate "#" (termName <$> parentAnnotations diffSummary) ++ " context" - (Delete termInfo) -> "Deleted " ++ "'" ++ termName termInfo ++ "' " - ++ "in the " ++ intercalate "#" (termName <$> parentAnnotations diffSummary) ++ " context" + show DiffSummary{..} = case patch of + (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo + ++ if null parentAnnotations then "" else " to the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" + (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo + ++ if null parentAnnotations then "" else " in the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" (Replace t1 t2) -> "Replaced " -diffSummary :: Show leaf => Diff leaf Info -> [DiffSummary DiffInfo] +diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where - diffSummary' :: Show leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] + diffSummary' :: IsTerm leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes - diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join children - diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join children - diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo . termName . toCategory $ snd infos) <$> join (F.toList children) - diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toTermName term))) []] - diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toTermName term))) []] - diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toTermName t1)) (DiffInfo (toTermName t2))) []] + diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join (F.toList children) + diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toTermName term) (toTerm term))) []] + diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toTermName term) (toTerm term))) []] + diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toTermName t1) (toTerm t1)) (DiffInfo (toTermName t2) (toTerm t2))) []] prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } -toTermName :: Show leaf => Term leaf Info -> String +toTermName :: IsTerm leaf => Term leaf Info -> String toTermName term = termName $ case runCofree term of (info :< Leaf _) -> toCategory info (info :< Indexed _) -> toCategory info diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index ce22c6efb..bb59e343f 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -22,13 +22,13 @@ testDiff :: Diff String Info testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) testSummary :: DiffSummary Char -testSummary = DiffSummary { patch = Insert (DiffInfo "string literal"), parentAnnotations = [] } +testSummary = DiffSummary { patch = Insert (DiffInfo "string literal" (Just "a")), parentAnnotations = [] } spec :: Spec spec = parallel $ do describe "diffSummary" $ do it "outputs a diff summary" $ do - diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string literal"), parentAnnotations = [ DiffInfo "array literal" ] } ] + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string literal" (Just "a")), parentAnnotations = [ DiffInfo "array literal" Nothing ] } ] describe "show" $ do it "should print adds" $ - show testSummary `shouldBe` "Added an 'a' expression" + show testSummary `shouldBe` "Added the 'a' string literal" From aade9415aa5684262dbe4d7511b20571c88fc88c Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 10:24:08 -0400 Subject: [PATCH 32/53] Add replacement summaries --- src/DiffSummary.hs | 4 +++- test/DiffSummarySpec.hs | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 260968770..47c6b9061 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -63,7 +63,9 @@ instance Show a => Show (DiffSummary a) where ++ if null parentAnnotations then "" else " to the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo ++ if null parentAnnotations then "" else " in the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" - (Replace t1 t2) -> "Replaced " + (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (term t1) ++ "' " ++ termName t1 + ++ " with the " ++ "'" ++ fromJust (term t2) ++ "' " ++ termName t2 + ++ if null parentAnnotations then "" else " in the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index bb59e343f..92ce568aa 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -24,6 +24,9 @@ testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree testSummary :: DiffSummary Char testSummary = DiffSummary { patch = Insert (DiffInfo "string literal" (Just "a")), parentAnnotations = [] } +replacementSummary :: DiffSummary Char +replacementSummary = DiffSummary { patch = Replace (DiffInfo "string literal" (Just "a")) (DiffInfo "symbol literal" (Just "b")), parentAnnotations = [ (DiffInfo "array literal" Nothing)] } + spec :: Spec spec = parallel $ do describe "diffSummary" $ do @@ -32,3 +35,5 @@ spec = parallel $ do describe "show" $ do it "should print adds" $ show testSummary `shouldBe` "Added the 'a' string literal" + it "prints a replacement" $ do + show replacementSummary `shouldBe` "Replaced the 'a' string literal with the 'b' symbol literal in the array literal context" From 68948287f8ce98fe67c343bb82c7d8cdd74be429 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 12:01:16 -0400 Subject: [PATCH 33/53] Add a summary renderer --- semantic-diff.cabal | 1 + src/DiffOutput.hs | 4 ++++ src/DiffSummary.hs | 8 +++++--- src/Diffing.hs | 2 +- src/Renderer.hs | 4 ++-- src/Renderer/JSON.hs | 2 +- src/Renderer/Patch.hs | 3 +-- src/Renderer/Split.hs | 2 +- src/Renderer/Summary.hs | 8 ++++++++ test/CorpusSpec.hs | 6 +++--- 10 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 src/Renderer/Summary.hs diff --git a/semantic-diff.cabal b/semantic-diff.cabal index b7db4b044..05ab50f28 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -39,6 +39,7 @@ library , Renderer.JSON , Renderer.Patch , Renderer.Split + , Renderer.Summary , SES , Source , SplitDiff diff --git a/src/DiffOutput.hs b/src/DiffOutput.hs index d7ab99e95..e491bcf5e 100644 --- a/src/DiffOutput.hs +++ b/src/DiffOutput.hs @@ -6,6 +6,7 @@ import Diffing import Parser import qualified Renderer.JSON as J import qualified Renderer.Patch as P +import qualified Renderer.Summary as S import Renderer import Renderer.Split import Source @@ -20,6 +21,7 @@ textDiff parser arguments sources = case format arguments of Split -> diffFiles parser split sources Patch -> diffFiles parser P.patch sources JSON -> diffFiles parser J.json sources + Summary -> diffFiles parser S.summary sources -- | Returns a truncated diff given diff arguments and two source blobs. truncatedDiff :: DiffArguments -> Both SourceBlob -> IO Text @@ -27,6 +29,7 @@ truncatedDiff arguments sources = case format arguments of Split -> return "" Patch -> return $ P.truncatePatch arguments sources JSON -> return "{}" + Summary -> return "" -- | Prints a rendered diff to stdio or a filepath given a parser, diff arguments and two source blobs. printDiff :: Parser -> DiffArguments -> Both SourceBlob -> IO () @@ -42,3 +45,4 @@ printDiff parser arguments sources = case format arguments of IO.withFile outputPath IO.WriteMode (`TextIO.hPutStr` rendered) Patch -> TextIO.putStr =<< diffFiles parser P.patch sources JSON -> TextIO.putStr =<< diffFiles parser J.json sources + Summary -> TextIO.putStr =<< diffFiles parser S.summary sources diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 47c6b9061..135b8e913 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -8,9 +8,7 @@ import Patch import Term import Syntax import Category -import Data.Maybe (fromMaybe) -import Data.Set (toList) -import Control.Comonad + import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import Control.Monad @@ -19,6 +17,7 @@ import Data.List import Data.Functor.Foldable as Foldable import Data.Functor.Both import qualified Data.Foldable as F +import Data.Text as Text (unpack, Text) data DiffInfo = DiffInfo { name :: String, term :: Maybe String } deriving (Eq, Show) @@ -40,6 +39,9 @@ instance IsTerm DiffInfo where instance IsTerm String where termName = id +instance IsTerm Text where + termName = unpack + instance IsTerm Category where termName category = case category of BinaryOperator -> "binary operator" diff --git a/src/Diffing.hs b/src/Diffing.hs index 8cb163926..98c44bf6b 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -81,7 +81,7 @@ readAndTranscodeFile path = do -- | result. -- | Returns the rendered result strictly, so it's always fully evaluated -- | with respect to other IO actions. -diffFiles :: Parser -> Renderer T.Text -> Both SourceBlob -> IO T.Text +diffFiles :: Parser -> Renderer -> Both SourceBlob -> IO T.Text diffFiles parser renderer sourceBlobs = do let sources = source <$> sourceBlobs terms <- sequence $ parser <$> sources diff --git a/src/Renderer.hs b/src/Renderer.hs index aaddf47af..db2406a34 100644 --- a/src/Renderer.hs +++ b/src/Renderer.hs @@ -7,11 +7,11 @@ import Source import Data.Text -- | A function that will render a diff, given the two source files. -type Renderer a = Diff a Info -> Both SourceBlob -> Text +type Renderer = Diff Text Info -> Both SourceBlob -> Text data DiffArguments = DiffArguments { format :: Format, output :: Maybe FilePath, outputPath :: FilePath } deriving (Show) -- | The available types of diff rendering. -data Format = Split | Patch | JSON +data Format = Split | Patch | JSON | Summary deriving (Show) diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index e720ad99e..03dc7926d 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -26,7 +26,7 @@ import Syntax import Term -- | Render a diff to a string representing its JSON. -json :: Renderer a +json :: Renderer json diff sources = toStrict . toLazyText . encodeToTextBuilder $ object ["rows" .= annotateRows (splitDiffByLines (source <$> sources) diff), "oids" .= (oid <$> sources), "paths" .= (path <$> sources)] where annotateRows = fmap (fmap NumberedLine) . numberedRows diff --git a/src/Renderer/Patch.hs b/src/Renderer/Patch.hs index c00d27b30..67fe1a02e 100644 --- a/src/Renderer/Patch.hs +++ b/src/Renderer/Patch.hs @@ -16,7 +16,6 @@ import Renderer import Source hiding ((++), break) import SplitDiff import Control.Comonad.Trans.Cofree -import Data.Functor.Foldable import Control.Monad.Trans.Free import Data.Functor.Both as Both import Data.List @@ -29,7 +28,7 @@ truncatePatch :: DiffArguments -> Both SourceBlob -> Text truncatePatch _ blobs = pack $ header blobs ++ "#timed_out\nTruncating diff: timeout reached.\n" -- | Render a diff in the traditional patch format. -patch :: Renderer a +patch :: Renderer patch diff blobs = pack $ case getLast (foldMap (Last . Just) string) of Just c | c /= '\n' -> string ++ "\n\\ No newline at end of file\n" _ -> string diff --git a/src/Renderer/Split.hs b/src/Renderer/Split.hs index 33ff75bf0..904917d64 100644 --- a/src/Renderer/Split.hs +++ b/src/Renderer/Split.hs @@ -52,7 +52,7 @@ splitPatchToClassName patch = stringValue $ "patch " ++ case patch of SplitReplace _ -> "replace" -- | Render a diff as an HTML split diff. -split :: Renderer leaf +split :: Renderer split diff blobs = TL.toStrict . renderHtml . docTypeHtml . ((head $ link ! A.rel "stylesheet" ! A.href "style.css") <>) diff --git a/src/Renderer/Summary.hs b/src/Renderer/Summary.hs new file mode 100644 index 000000000..926fd96af --- /dev/null +++ b/src/Renderer/Summary.hs @@ -0,0 +1,8 @@ +module Renderer.Summary where + +import Renderer +import DiffSummary +import Data.Text (pack) + +summary :: Renderer +summary diff sources = pack . show $ diffSummary diff diff --git a/test/CorpusSpec.hs b/test/CorpusSpec.hs index 2ddc22a61..9dda18b83 100644 --- a/test/CorpusSpec.hs +++ b/test/CorpusSpec.hs @@ -40,10 +40,10 @@ spec = parallel $ do let tests = correctTests =<< paths mapM_ (\ (formatName, renderer, paths, output) -> it (normalizeName (fst paths) ++ " (" ++ formatName ++ ")") $ testDiff renderer paths output matcher) tests - correctTests :: (Both FilePath, Maybe FilePath, Maybe FilePath, Maybe FilePath) -> [(String, Renderer a, Both FilePath, Maybe FilePath)] + correctTests :: (Both FilePath, Maybe FilePath, Maybe FilePath, Maybe FilePath) -> [(String, Renderer, Both FilePath, Maybe FilePath)] correctTests paths@(_, Nothing, Nothing, Nothing) = testsForPaths paths correctTests paths = List.filter (\(_, _, _, output) -> isJust output) $ testsForPaths paths - testsForPaths :: (Both FilePath, Maybe FilePath, Maybe FilePath, Maybe FilePath) -> [(String, Renderer a, Both FilePath, Maybe FilePath)] + testsForPaths :: (Both FilePath, Maybe FilePath, Maybe FilePath, Maybe FilePath) -> [(String, Renderer, Both FilePath, Maybe FilePath)] testsForPaths (paths, json, patch, split) = [ ("json", J.json, paths, json), ("patch", P.patch, paths, patch), ("split", Split.split, paths, split) ] -- | Return all the examples from the given directory. Examples are expected to @@ -70,7 +70,7 @@ normalizeName path = addExtension (dropExtension $ dropExtension path) (takeExte -- | Given file paths for A, B, and, optionally, a diff, return whether diffing -- | the files will produce the diff. If no diff is provided, then the result -- | is true, but the diff will still be calculated. -testDiff :: Renderer T.Text -> Both FilePath -> Maybe FilePath -> (T.Text -> T.Text -> Expectation) -> Expectation +testDiff :: Renderer -> Both FilePath -> Maybe FilePath -> (T.Text -> T.Text -> Expectation) -> Expectation testDiff renderer paths diff matcher = do sources <- sequence $ readAndTranscodeFile <$> paths actual <- diffFiles parser renderer (sourceBlobs sources) From f0b57605022ba39e88683a11a224a800684d1dc6 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 13:27:19 -0400 Subject: [PATCH 34/53] Extract the first category for branch nodes --- src/DiffSummary.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 135b8e913..85f1b2118 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -8,7 +8,7 @@ import Patch import Term import Syntax import Category - +import Control.Comonad import Control.Comonad.Trans.Cofree import Control.Monad.Trans.Free import Control.Monad @@ -16,6 +16,7 @@ import Data.Maybe import Data.List import Data.Functor.Foldable as Foldable import Data.Functor.Both +import Data.OrderedMap import qualified Data.Foldable as F import Data.Text as Text (unpack, Text) @@ -28,7 +29,10 @@ class ToTerm a where instance IsTerm leaf => ToTerm (Term leaf Info) where toTerm term = case runCofree term of (_ :< Leaf leaf) -> Just (termName leaf) - _ -> Nothing + (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) + (_ :< Indexed children) -> Just (termName . toCategory . head $ extract <$> children) + (_ :< Fixed children) -> Just (termName . toCategory . head $ extract <$> children) + class IsTerm a where termName :: a -> String From ea87d385a59a828973ba5e1cd6a2cf22f8a255a7 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 18 May 2016 13:37:02 -0400 Subject: [PATCH 35/53] tweak separators --- src/DiffSummary.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 85f1b2118..19d235c0b 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -49,13 +49,13 @@ instance IsTerm Text where instance IsTerm Category where termName category = case category of BinaryOperator -> "binary operator" - DictionaryLiteral -> "dictionary literal" + DictionaryLiteral -> "dictionary" Pair -> "pair" FunctionCall -> "function call" - StringLiteral -> "string literal" - IntegerLiteral -> "integer literal" - SymbolLiteral -> "symbol literal" - ArrayLiteral -> "array literal" + StringLiteral -> "string" + IntegerLiteral -> "integer" + SymbolLiteral -> "symbol" + ArrayLiteral -> "array" (Other s) -> s data DiffSummary a = DiffSummary { @@ -66,12 +66,12 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo - ++ if null parentAnnotations then "" else " to the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" + ++ if null parentAnnotations then "" else " to the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo - ++ if null parentAnnotations then "" else " in the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" + ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (term t1) ++ "' " ++ termName t1 ++ " with the " ++ "'" ++ fromJust (term t2) ++ "' " ++ termName t2 - ++ if null parentAnnotations then "" else " in the " ++ intercalate "#" (termName <$> parentAnnotations) ++ " context" + ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where From 72dea795d2bc2172d26722c5ca69e7a20788c3de Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 23 May 2016 13:57:04 -0400 Subject: [PATCH 36/53] Remove literal string from tests --- src/DiffSummary.hs | 14 ++++++-------- test/DiffSummarySpec.hs | 10 +++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 19d235c0b..662edd9f4 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -20,12 +20,11 @@ import Data.OrderedMap import qualified Data.Foldable as F import Data.Text as Text (unpack, Text) -data DiffInfo = DiffInfo { name :: String, term :: Maybe String } deriving (Eq, Show) +data DiffInfo = DiffInfo { categoryName :: String, value :: Maybe String } deriving (Eq, Show) class ToTerm a where toTerm :: a -> Maybe String - instance IsTerm leaf => ToTerm (Term leaf Info) where toTerm term = case runCofree term of (_ :< Leaf leaf) -> Just (termName leaf) @@ -33,12 +32,11 @@ instance IsTerm leaf => ToTerm (Term leaf Info) where (_ :< Indexed children) -> Just (termName . toCategory . head $ extract <$> children) (_ :< Fixed children) -> Just (termName . toCategory . head $ extract <$> children) - class IsTerm a where termName :: a -> String instance IsTerm DiffInfo where - termName = name + termName = categoryName instance IsTerm String where termName = id @@ -65,12 +63,12 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of - (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo + (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ termName termInfo ++ if null parentAnnotations then "" else " to the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" - (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (term termInfo) ++ "' " ++ termName termInfo + (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ termName termInfo ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" - (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (term t1) ++ "' " ++ termName t1 - ++ " with the " ++ "'" ++ fromJust (term t2) ++ "' " ++ termName t2 + (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ termName t1 + ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ termName t2 ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 92ce568aa..95488af0e 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -22,18 +22,18 @@ testDiff :: Diff String Info testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) testSummary :: DiffSummary Char -testSummary = DiffSummary { patch = Insert (DiffInfo "string literal" (Just "a")), parentAnnotations = [] } +testSummary = DiffSummary { patch = Insert (DiffInfo "string" (Just "a")), parentAnnotations = [] } replacementSummary :: DiffSummary Char -replacementSummary = DiffSummary { patch = Replace (DiffInfo "string literal" (Just "a")) (DiffInfo "symbol literal" (Just "b")), parentAnnotations = [ (DiffInfo "array literal" Nothing)] } +replacementSummary = DiffSummary { patch = Replace (DiffInfo "string" (Just "a")) (DiffInfo "symbol" (Just "b")), parentAnnotations = [ (DiffInfo "array" (Just "switch {}")) ] } spec :: Spec spec = parallel $ do describe "diffSummary" $ do it "outputs a diff summary" $ do - diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string literal" (Just "a")), parentAnnotations = [ DiffInfo "array literal" Nothing ] } ] + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string" (Just "a")), parentAnnotations = [ DiffInfo "array" Nothing ] } ] describe "show" $ do it "should print adds" $ - show testSummary `shouldBe` "Added the 'a' string literal" + show testSummary `shouldBe` "Added the 'a' string" it "prints a replacement" $ do - show replacementSummary `shouldBe` "Replaced the 'a' string literal with the 'b' symbol literal in the array literal context" + show replacementSummary `shouldBe` "Replaced the 'a' string with the 'b' symbol in the array context" From 106686ce1ff9715aa5420e0ecb1dcf6dea4ddf98 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 24 May 2016 12:33:53 -0400 Subject: [PATCH 37/53] Remove IsTerm DiffInfo instance --- src/DiffSummary.hs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 662edd9f4..d33a95ba2 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -35,9 +35,6 @@ instance IsTerm leaf => ToTerm (Term leaf Info) where class IsTerm a where termName :: a -> String -instance IsTerm DiffInfo where - termName = categoryName - instance IsTerm String where termName = id @@ -63,13 +60,13 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of - (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ termName termInfo - ++ if null parentAnnotations then "" else " to the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" - (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ termName termInfo - ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" - (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ termName t1 - ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ termName t2 - ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (termName <$> parentAnnotations) ++ " context" + (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo + ++ if null parentAnnotations then "" else " to the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo + ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ categoryName t1 + ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ categoryName t2 + ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where From 56cfccac1987635740f6c73d45e59526716c6106 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 24 May 2016 12:37:44 -0400 Subject: [PATCH 38/53] Move if conditions to maybeParentContext --- src/DiffSummary.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index d33a95ba2..fee8d1408 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -61,12 +61,15 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo - ++ if null parentAnnotations then "" else " to the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + ++ maybeParentContext parentAnnotations (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo - ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + ++ maybeParentContext parentAnnotations (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ categoryName t1 ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ categoryName t2 - ++ if null parentAnnotations then "" else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + ++ maybeParentContext parentAnnotations + where maybeParentContext parentAnnotations = if null parentAnnotations + then "" + else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where From eeda4c6774f615c0747e4869f1f1fb6f38626d9a Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 24 May 2016 12:41:57 -0400 Subject: [PATCH 39/53] Add an IsTerm instance for Term leaf Info --- src/DiffSummary.hs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index fee8d1408..b0679208a 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -53,6 +53,13 @@ instance IsTerm Category where ArrayLiteral -> "array" (Other s) -> s +instance IsTerm leaf => IsTerm (Term leaf Info) where + termName term = termName $ case runCofree term of + (info :< Leaf _) -> toCategory info + (info :< Indexed _) -> toCategory info + (info :< Fixed _) -> toCategory info + (info :< Keyed _) -> toCategory info + data DiffSummary a = DiffSummary { patch :: Patch DiffInfo, parentAnnotations :: [DiffInfo] @@ -78,19 +85,12 @@ diffSummary = cata diffSummary' where diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join (F.toList children) - diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toTermName term) (toTerm term))) []] - diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toTermName term) (toTerm term))) []] - diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toTermName t1) (toTerm t1)) (DiffInfo (toTermName t2) (toTerm t2))) []] + diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (termName term) (toTerm term))) []] + diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (termName term) (toTerm term))) []] + diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (termName t1) (toTerm t1)) (DiffInfo (termName t2) (toTerm t2))) []] prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } -toTermName :: IsTerm leaf => Term leaf Info -> String -toTermName term = termName $ case runCofree term of - (info :< Leaf _) -> toCategory info - (info :< Indexed _) -> toCategory info - (info :< Fixed _) -> toCategory info - (info :< Keyed _) -> toCategory info - toCategory :: Info -> Category toCategory info = fromMaybe (Other "Unknown") (maybeFirstCategory info) From d073b1cf8508eea68976d0a9a80b774d97e7faa3 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 24 May 2016 12:49:16 -0400 Subject: [PATCH 40/53] Rename IsTerm to HasCategory --- src/DiffSummary.hs | 57 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index b0679208a..25dc4e3c3 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -20,29 +20,29 @@ import Data.OrderedMap import qualified Data.Foldable as F import Data.Text as Text (unpack, Text) -data DiffInfo = DiffInfo { categoryName :: String, value :: Maybe String } deriving (Eq, Show) +data DiffInfo = DiffInfo { category :: String, value :: Maybe String } deriving (Eq, Show) class ToTerm a where toTerm :: a -> Maybe String -instance IsTerm leaf => ToTerm (Term leaf Info) where +instance HasCategory leaf => ToTerm (Term leaf Info) where toTerm term = case runCofree term of - (_ :< Leaf leaf) -> Just (termName leaf) + (_ :< Leaf leaf) -> Just (categoryName leaf) (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) - (_ :< Indexed children) -> Just (termName . toCategory . head $ extract <$> children) - (_ :< Fixed children) -> Just (termName . toCategory . head $ extract <$> children) + (_ :< Indexed children) -> Just (categoryName . toCategory . head $ extract <$> children) + (_ :< Fixed children) -> Just (categoryName . toCategory . head $ extract <$> children) -class IsTerm a where - termName :: a -> String +class HasCategory a where + categoryName :: a -> String -instance IsTerm String where - termName = id +instance HasCategory String where + categoryName = id -instance IsTerm Text where - termName = unpack +instance HasCategory Text where + categoryName = unpack -instance IsTerm Category where - termName category = case category of +instance HasCategory Category where + categoryName category = case category of BinaryOperator -> "binary operator" DictionaryLiteral -> "dictionary" Pair -> "pair" @@ -53,13 +53,14 @@ instance IsTerm Category where ArrayLiteral -> "array" (Other s) -> s -instance IsTerm leaf => IsTerm (Term leaf Info) where - termName term = termName $ case runCofree term of +instance HasCategory leaf => HasCategory (Term leaf Info) where + categoryName term = categoryName $ case runCofree term of (info :< Leaf _) -> toCategory info (info :< Indexed _) -> toCategory info (info :< Fixed _) -> toCategory info (info :< Keyed _) -> toCategory info + data DiffSummary a = DiffSummary { patch :: Patch DiffInfo, parentAnnotations :: [DiffInfo] @@ -67,27 +68,27 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of - (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo + (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ category termInfo ++ maybeParentContext parentAnnotations - (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ categoryName termInfo + (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ category termInfo ++ maybeParentContext parentAnnotations - (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ categoryName t1 - ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ categoryName t2 + (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ category t1 + ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ category t2 ++ maybeParentContext parentAnnotations where maybeParentContext parentAnnotations = if null parentAnnotations then "" - else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" + else " in the " ++ intercalate "/" (category <$> parentAnnotations) ++ " context" -diffSummary :: IsTerm leaf => Diff leaf Info -> [DiffSummary DiffInfo] +diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where - diffSummary' :: IsTerm leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] + diffSummary' :: HasCategory leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes - diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children - diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join children - diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (termName . toCategory $ snd infos) Nothing) <$> join (F.toList children) - diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (termName term) (toTerm term))) []] - diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (termName term) (toTerm term))) []] - diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (termName t1) (toTerm t1)) (DiffInfo (termName t2) (toTerm t2))) []] + diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join (F.toList children) + diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (categoryName term) (toTerm term))) []] + diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (categoryName term) (toTerm term))) []] + diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (categoryName t1) (toTerm t1)) (DiffInfo (categoryName t2) (toTerm t2))) []] prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } From cbf09d19b9e75ea56b04748398596398a24e57b6 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 24 May 2016 13:06:15 -0400 Subject: [PATCH 41/53] s/categoryName/toCategoryName value/termName --- src/DiffSummary.hs | 49 ++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 25dc4e3c3..55a0da2cc 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -20,29 +20,26 @@ import Data.OrderedMap import qualified Data.Foldable as F import Data.Text as Text (unpack, Text) -data DiffInfo = DiffInfo { category :: String, value :: Maybe String } deriving (Eq, Show) +data DiffInfo = DiffInfo { categoryName :: String, termName :: Maybe String } deriving (Eq, Show) -class ToTerm a where - toTerm :: a -> Maybe String - -instance HasCategory leaf => ToTerm (Term leaf Info) where - toTerm term = case runCofree term of - (_ :< Leaf leaf) -> Just (categoryName leaf) - (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) - (_ :< Indexed children) -> Just (categoryName . toCategory . head $ extract <$> children) - (_ :< Fixed children) -> Just (categoryName . toCategory . head $ extract <$> children) +maybeTermName :: HasCategory leaf => Term leaf Info -> Maybe String +maybeTermName term = case runCofree term of + (_ :< Leaf leaf) -> Just (toCategoryName leaf) + (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) + (_ :< Indexed children) -> Just (toCategoryName . toCategory . head $ extract <$> children) + (_ :< Fixed children) -> Just (toCategoryName . toCategory . head $ extract <$> children) class HasCategory a where - categoryName :: a -> String + toCategoryName :: a -> String instance HasCategory String where - categoryName = id + toCategoryName = id instance HasCategory Text where - categoryName = unpack + toCategoryName = unpack instance HasCategory Category where - categoryName category = case category of + toCategoryName category = case category of BinaryOperator -> "binary operator" DictionaryLiteral -> "dictionary" Pair -> "pair" @@ -54,7 +51,7 @@ instance HasCategory Category where (Other s) -> s instance HasCategory leaf => HasCategory (Term leaf Info) where - categoryName term = categoryName $ case runCofree term of + toCategoryName term = toCategoryName $ case runCofree term of (info :< Leaf _) -> toCategory info (info :< Indexed _) -> toCategory info (info :< Fixed _) -> toCategory info @@ -68,27 +65,27 @@ data DiffSummary a = DiffSummary { instance Show a => Show (DiffSummary a) where show DiffSummary{..} = case patch of - (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ category termInfo + (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (termName termInfo) ++ "' " ++ categoryName termInfo ++ maybeParentContext parentAnnotations - (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (value termInfo) ++ "' " ++ category termInfo + (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (termName termInfo) ++ "' " ++ categoryName termInfo ++ maybeParentContext parentAnnotations - (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (value t1) ++ "' " ++ category t1 - ++ " with the " ++ "'" ++ fromJust (value t2) ++ "' " ++ category t2 + (Replace t1 t2) -> "Replaced the " ++ "'" ++ fromJust (termName t1) ++ "' " ++ categoryName t1 + ++ " with the " ++ "'" ++ fromJust (termName t2) ++ "' " ++ categoryName t2 ++ maybeParentContext parentAnnotations where maybeParentContext parentAnnotations = if null parentAnnotations then "" - else " in the " ++ intercalate "/" (category <$> parentAnnotations) ++ " context" + else " in the " ++ intercalate "/" (categoryName <$> parentAnnotations) ++ " context" diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where diffSummary' :: HasCategory leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes - diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join children - diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join children - diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (categoryName . toCategory $ snd infos) Nothing) <$> join (F.toList children) - diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (categoryName term) (toTerm term))) []] - diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (categoryName term) (toTerm term))) []] - diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (categoryName t1) (toTerm t1)) (DiffInfo (categoryName t2) (toTerm t2))) []] + diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children + diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join (F.toList children) + diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toCategoryName term) (maybeTermName term))) []] + diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toCategoryName term) (maybeTermName term))) []] + diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toCategoryName t1) (maybeTermName t1)) (DiffInfo (toCategoryName t2) (maybeTermName t2))) []] prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } From e027e81f3d74c45bbc7d0d8b6205ba5d1d349833 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 11:34:02 -0400 Subject: [PATCH 42/53] Add a Show instance to language --- src/Language.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Language.hs b/src/Language.hs index c63138bd2..bf08e6bed 100644 --- a/src/Language.hs +++ b/src/Language.hs @@ -20,6 +20,7 @@ data Language = | R | Ruby | Swift + deriving (Show) -- | Returns a Language based on the file extension (including the "."). languageForType :: Text -> Maybe Language From 2f761fc38b213bc45ac427aa41cdf73a0a09f657 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 13:44:17 -0400 Subject: [PATCH 43/53] Remove split dependency --- semantic-diff.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index d61e730b4..4e8f6e454 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -64,7 +64,6 @@ library , recursion-schemes , free , comonad - , split , protolude default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, OverloadedStrings, NoImplicitPrelude From b1f9cc4a4aef4d4e02204e5ea5dd7e455e7e80e1 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 13:46:33 -0400 Subject: [PATCH 44/53] Remove DiffF type synonym --- src/Diff.hs | 1 - src/DiffSummary.hs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Diff.hs b/src/Diff.hs index 4250236a6..88e8251c2 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -21,7 +21,6 @@ annotate :: annotation -> Syntax a f -> CofreeF (Syntax a) annotation f annotate = (:<) -- | An annotated series of patches of terms. -type DiffF a annotation = FreeF (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) type instance Base (Free f a) = FreeF f a diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 55a0da2cc..503dd64ff 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -78,7 +78,7 @@ instance Show a => Show (DiffSummary a) where diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata diffSummary' where - diffSummary' :: HasCategory leaf => DiffF leaf Info [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] + diffSummary' :: HasCategory leaf => Base (Diff leaf Info) [DiffSummary DiffInfo] -> [DiffSummary DiffInfo] diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children From ebd5d20171b388577b01af098f6a4be7834fe78b Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:04:27 -0400 Subject: [PATCH 45/53] Remove duplicate imports --- src/DiffSummary.hs | 23 +++++++++-------------- src/Interpreter.hs | 4 ---- src/Prologue.hs | 1 + src/Renderer/JSON.hs | 2 -- src/Term.hs | 4 ---- test/AlignmentSpec.hs | 4 ---- test/ArbitraryTerm.hs | 4 ---- test/InterpreterSpec.hs | 2 -- 8 files changed, 10 insertions(+), 34 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 503dd64ff..6abaa5cf6 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,24 +1,19 @@ {-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables, FlexibleInstances, RecordWildCards #-} module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where -import Prelude hiding (fst, snd) +import Prologue hiding (fst, snd) +import Data.String +import Data.Maybe (fromJust) import Diff import Info import Patch import Term import Syntax import Category -import Control.Comonad -import Control.Comonad.Trans.Cofree -import Control.Monad.Trans.Free -import Control.Monad -import Data.Maybe -import Data.List import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.OrderedMap -import qualified Data.Foldable as F -import Data.Text as Text (unpack, Text) +import Data.Text as Text (unpack) data DiffInfo = DiffInfo { categoryName :: String, termName :: Maybe String } deriving (Eq, Show) @@ -26,14 +21,14 @@ maybeTermName :: HasCategory leaf => Term leaf Info -> Maybe String maybeTermName term = case runCofree term of (_ :< Leaf leaf) -> Just (toCategoryName leaf) (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) - (_ :< Indexed children) -> Just (toCategoryName . toCategory . head $ extract <$> children) - (_ :< Fixed children) -> Just (toCategoryName . toCategory . head $ extract <$> children) + (_ :< Indexed children) -> Just (toCategoryName . maybe (Other "Unknown") toCategory . head $ extract <$> children) + (_ :< Fixed children) -> Just (toCategoryName . maybe (Other "Unknown") toCategory . head $ extract <$> children) class HasCategory a where toCategoryName :: a -> String instance HasCategory String where - toCategoryName = id + toCategoryName = identity instance HasCategory Text where toCategoryName = unpack @@ -64,7 +59,7 @@ data DiffSummary a = DiffSummary { } deriving (Eq, Functor) instance Show a => Show (DiffSummary a) where - show DiffSummary{..} = case patch of + showsPrec _ DiffSummary{..} s = (++s) $ case patch of (Insert termInfo) -> "Added the " ++ "'" ++ fromJust (termName termInfo) ++ "' " ++ categoryName termInfo ++ maybeParentContext parentAnnotations (Delete termInfo) -> "Deleted the " ++ "'" ++ fromJust (termName termInfo) ++ "' " ++ categoryName termInfo @@ -82,7 +77,7 @@ diffSummary = cata diffSummary' where diffSummary' (Free (_ :< Leaf _)) = [] -- Skip leaves since they don't have any changes diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join children - diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join (F.toList children) + diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join (Prologue.toList children) diffSummary' (Pure (Insert term)) = [DiffSummary (Insert (DiffInfo (toCategoryName term) (maybeTermName term))) []] diffSummary' (Pure (Delete term)) = [DiffSummary (Delete (DiffInfo (toCategoryName term) (maybeTermName term))) []] diffSummary' (Pure (Replace t1 t2)) = [DiffSummary (Replace (DiffInfo (toCategoryName t1) (maybeTermName t1)) (DiffInfo (toCategoryName t2) (maybeTermName t2))) []] diff --git a/src/Interpreter.hs b/src/Interpreter.hs index 5143f8aab..0e0bb7fc3 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -3,14 +3,10 @@ module Interpreter (interpret, Comparable, diffTerms) where import Algorithm import Category import Data.Functor.Foldable -import Control.Comonad -import Control.Monad.Trans.Free -import Control.Comonad.Trans.Cofree import Data.Functor.Both import qualified Data.OrderedMap as Map import qualified Data.List as List import Data.List ((\\)) -import Data.Maybe import Data.OrderedMap ((!)) import Diff import Operation diff --git a/src/Prologue.hs b/src/Prologue.hs index a060ee48c..6111a371d 100644 --- a/src/Prologue.hs +++ b/src/Prologue.hs @@ -5,3 +5,4 @@ import Data.List (lookup) import System.IO (FilePath) import Control.Comonad.Trans.Cofree as X import Control.Monad.Trans.Free as X +import Control.Comonad as X diff --git a/src/Renderer/JSON.hs b/src/Renderer/JSON.hs index a44d42f8f..1fd1d5f5e 100644 --- a/src/Renderer/JSON.hs +++ b/src/Renderer/JSON.hs @@ -7,8 +7,6 @@ module Renderer.JSON ( import Prologue hiding (toList) import Alignment import Category -import Control.Comonad.Trans.Cofree -import Control.Monad.Trans.Free import Data.Aeson hiding (json) import Data.Aeson.Encode import Data.Functor.Both diff --git a/src/Term.hs b/src/Term.hs index 3a443b835..cb71caee1 100644 --- a/src/Term.hs +++ b/src/Term.hs @@ -32,10 +32,6 @@ zipTerms t1 t2 = annotate (zipUnwrap a b) zipUnwrap _ _ = Nothing zipUnwrapMaps a' b' key = (,) key <$> zipTerms (a' ! key) (b' ! key) --- | Fold a term into some other value, starting with the leaves. --- cata :: (annotation -> Syntax a b -> b) -> Term a annotation -> b --- cata f (annotation :< syntax) = f annotation $ cata f <$> syntax - -- | Return the node count of a term. termSize :: Term a annotation -> Integer termSize = cata size where diff --git a/test/AlignmentSpec.hs b/test/AlignmentSpec.hs index 580559d76..cae15b424 100644 --- a/test/AlignmentSpec.hs +++ b/test/AlignmentSpec.hs @@ -8,14 +8,10 @@ import Data.Text.Arbitrary () import Alignment import ArbitraryTerm (arbitraryLeaf) import Control.Arrow -import Control.Comonad.Trans.Cofree -import Control.Monad.Trans.Free hiding (unfold) import Data.Adjoined -import Data.Copointed import Data.Functor.Both as Both import Diff import Info -import qualified Data.Maybe as Maybe import Line import Patch import Prologue hiding (fst, snd) diff --git a/test/ArbitraryTerm.hs b/test/ArbitraryTerm.hs index 44a7afc82..a42d2ee45 100644 --- a/test/ArbitraryTerm.hs +++ b/test/ArbitraryTerm.hs @@ -1,15 +1,12 @@ module ArbitraryTerm where import Category -import Control.Comonad.Trans.Cofree import Data.Functor.Foldable -import Control.Monad import Data.Functor.Both import qualified Data.OrderedMap as Map import qualified Data.List as List import qualified Data.Set as Set import Data.Text.Arbitrary () -import Diff import Info import Line import Patch @@ -17,7 +14,6 @@ import Prologue hiding (fst, snd) import Range import Source hiding ((++)) import Syntax -import GHC.Generics import Term import Test.QuickCheck hiding (Fixed) diff --git a/test/InterpreterSpec.hs b/test/InterpreterSpec.hs index cde1f8b25..d4c9df2c2 100644 --- a/test/InterpreterSpec.hs +++ b/test/InterpreterSpec.hs @@ -5,8 +5,6 @@ import Diff import qualified Interpreter as I import Range import Syntax -import Control.Comonad.Trans.Cofree -import Control.Monad.Trans.Free import Patch import Info import Category From aeb747179f3684e8de8b53be2f172efcfa5a4bb9 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:05:25 -0400 Subject: [PATCH 46/53] Remove Annotated --- src/Diff.hs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Diff.hs b/src/Diff.hs index 88e8251c2..5af008a01 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -8,15 +8,6 @@ import Patch import Syntax import Term --- | An annotated syntax in a diff tree. -type Annotated a annotation f = CofreeF (Syntax a) annotation f - -annotation :: Annotated a annotation f -> annotation -annotation = headF - -syntax :: Annotated a annotation f -> Syntax a f -syntax = tailF - annotate :: annotation -> Syntax a f -> CofreeF (Syntax a) annotation f annotate = (:<) From 1245f3361a493318753b2c15cccd6ca380dd2cf1 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:08:26 -0400 Subject: [PATCH 47/53] We just say extract --- src/DiffSummary.hs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 6abaa5cf6..de02b8413 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -46,12 +46,7 @@ instance HasCategory Category where (Other s) -> s instance HasCategory leaf => HasCategory (Term leaf Info) where - toCategoryName term = toCategoryName $ case runCofree term of - (info :< Leaf _) -> toCategory info - (info :< Indexed _) -> toCategory info - (info :< Fixed _) -> toCategory info - (info :< Keyed _) -> toCategory info - + toCategoryName = toCategoryName . toCategory . extract data DiffSummary a = DiffSummary { patch :: Patch DiffInfo, From 9d9fbe3c3752e791ae0e4d87ce2e4d2786e57abd Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:46:13 -0400 Subject: [PATCH 48/53] :fire: annotate and unfix --- src/Diff.hs | 3 --- src/Interpreter.hs | 2 +- src/Term.hs | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Diff.hs b/src/Diff.hs index 5af008a01..d22604c19 100644 --- a/src/Diff.hs +++ b/src/Diff.hs @@ -8,9 +8,6 @@ import Patch import Syntax import Term -annotate :: annotation -> Syntax a f -> CofreeF (Syntax a) annotation f -annotate = (:<) - -- | An annotated series of patches of terms. type Diff a annotation = Free (CofreeF (Syntax a) (Both annotation)) (Patch (Term a annotation)) diff --git a/src/Interpreter.hs b/src/Interpreter.hs index 0e0bb7fc3..39444e35d 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -40,7 +40,7 @@ constructAndRun comparable cost t1 t2 = algorithm (Leaf a') (Leaf b') | a' == b' = annotate $ Leaf b' algorithm a' b' = free . Free $ Recursive (cofree (annotation1 :< a')) (cofree (annotation2 :< b')) (free . Pure) (annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2) - annotate = free . Pure . free . Free . Diff.annotate (both annotation1 annotation2) + annotate = free . Pure . free . Free . (:<) (both annotation1 annotation2) -- | Runs the diff algorithm run :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation) diff --git a/src/Term.hs b/src/Term.hs index cb71caee1..7ef888863 100644 --- a/src/Term.hs +++ b/src/Term.hs @@ -7,9 +7,6 @@ import Data.Functor.Both import Data.OrderedMap hiding (size) import Syntax -unfix :: Fix f -> f (Fix f) -unfix (Fix f) = f - -- | An annotated node (Syntax) in an abstract syntax tree. type TermF a annotation = CofreeF (Syntax a) annotation type Term a annotation = Cofree (Syntax a) annotation From a2c76a0ddcd096cf95036d33fcc69572070bfee7 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:49:27 -0400 Subject: [PATCH 49/53] fmap toCategoryName . toCategory over the head of children --- src/DiffSummary.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index de02b8413..a64e48749 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -21,8 +21,8 @@ maybeTermName :: HasCategory leaf => Term leaf Info -> Maybe String maybeTermName term = case runCofree term of (_ :< Leaf leaf) -> Just (toCategoryName leaf) (_ :< Keyed children) -> Just (unpack . mconcat $ keys children) - (_ :< Indexed children) -> Just (toCategoryName . maybe (Other "Unknown") toCategory . head $ extract <$> children) - (_ :< Fixed children) -> Just (toCategoryName . maybe (Other "Unknown") toCategory . head $ extract <$> children) + (_ :< Indexed children) -> toCategoryName . toCategory <$> head (extract <$> children) + (_ :< Fixed children) -> toCategoryName . toCategory <$> head (extract <$> children) class HasCategory a where toCategoryName :: a -> String From 500b75150356e5a70da63303db73dd2202986afc Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:53:12 -0400 Subject: [PATCH 50/53] Add use pure and use extract cases to HLint --- HLint.hs | 3 +++ src/Parser.hs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/HLint.hs b/HLint.hs index 438ff0e79..3e2a37f38 100644 --- a/HLint.hs +++ b/HLint.hs @@ -8,3 +8,6 @@ error "generalize forM_" = forM_ ==> for_ error "Avoid return" = return ==> pure where note = "return is obsolete as of GHC 7.10" + +error "use pure" = free . Pure ==> pure +error "use extract" = headF . runCofree ==> extract diff --git a/src/Parser.hs b/src/Parser.hs index 901760093..ed609c283 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -40,7 +40,7 @@ isFixed = not . Set.null . Set.intersection fixedCategories -- | Given a function that maps production names to sets of categories, produce -- | a Constructor. termConstructor :: (String -> Set.Set Category) -> Constructor -termConstructor mapping source range name children = cofree (Info range categories (1 + sum (size . headF . runCofree <$> children)) :< construct children) +termConstructor mapping source range name children = cofree (Info range categories (1 + sum (size . extract <$> children)) :< construct children) where categories = mapping name construct :: [Term Text Info] -> Syntax Text (Term Text Info) From 25be443f8a761eca85d272ee4dc6dd0f8638513d Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:54:47 -0400 Subject: [PATCH 51/53] s/free . Pure/pure --- src/Interpreter.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Interpreter.hs b/src/Interpreter.hs index 39444e35d..6e7628e31 100644 --- a/src/Interpreter.hs +++ b/src/Interpreter.hs @@ -25,7 +25,7 @@ diffTerms cost = interpret comparable cost -- | Diff two terms, given a function that determines whether two terms can be compared. interpret :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Diff a annotation -interpret comparable cost a b = fromMaybe (free . Pure $ Replace a b) $ constructAndRun comparable cost a b +interpret comparable cost a b = fromMaybe (pure $ Replace a b) $ constructAndRun comparable cost a b -- | Constructs an algorithm and runs it constructAndRun :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Term a annotation -> Term a annotation -> Maybe (Diff a annotation) @@ -38,9 +38,9 @@ constructAndRun comparable cost t1 t2 = algorithm (Indexed a') (Indexed b') = free . Free $ ByIndex a' b' (annotate . Indexed) algorithm (Keyed a') (Keyed b') = free . Free $ ByKey a' b' (annotate . Keyed) algorithm (Leaf a') (Leaf b') | a' == b' = annotate $ Leaf b' - algorithm a' b' = free . Free $ Recursive (cofree (annotation1 :< a')) (cofree (annotation2 :< b')) (free . Pure) + algorithm a' b' = free . Free $ Recursive (cofree (annotation1 :< a')) (cofree (annotation2 :< b')) pure (annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2) - annotate = free . Pure . free . Free . (:<) (both annotation1 annotation2) + annotate = pure . free . Free . (:<) (both annotation1 annotation2) -- | Runs the diff algorithm run :: (Eq a, Eq annotation) => Comparable a annotation -> Cost a annotation -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation) @@ -56,11 +56,11 @@ run comparable cost algorithm = case runFree algorithm of bKeys = Map.keys b' repack key = (key, interpretInBoth key a' b') interpretInBoth key x y = interpret comparable cost (x ! key) (y ! key) - recur _ _ = free . Pure $ Replace (cofree (annotation1 :< a)) (cofree (annotation2 :< b)) + recur _ _ = pure $ Replace (cofree (annotation1 :< a)) (cofree (annotation2 :< b)) Free (ByKey a b f) -> run comparable cost $ f byKey where byKey = Map.fromList $ toKeyValue <$> List.union aKeys bKeys - toKeyValue key | key `List.elem` deleted = (key, free . Pure . Delete $ a ! key) - toKeyValue key | key `List.elem` inserted = (key, free . Pure . Insert $ b ! key) + toKeyValue key | key `List.elem` deleted = (key, pure . Delete $ a ! key) + toKeyValue key | key `List.elem` inserted = (key, pure . Insert $ b ! key) toKeyValue key = (key, interpret comparable cost (a ! key) (b ! key)) aKeys = Map.keys a bKeys = Map.keys b From e3559641ff0d0366a002b1a0fa633b4767e04140 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:55:46 -0400 Subject: [PATCH 52/53] s/headF.runCofree/extract --- src/Diffing.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Diffing.hs b/src/Diffing.hs index c4e213d80..3f5c55d4c 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -57,7 +57,7 @@ breakDownLeavesByWord source = cata replaceIn , length ranges > 1 = cofree $ Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges) replaceIn (Info range categories _ :< syntax) - = cofree $ Info range categories (1 + sum (size . headF . runCofree <$> syntax)) :< syntax + = cofree $ Info range categories (1 + sum (size . extract <$> syntax)) :< syntax rangesAndWordsInSource range = rangesAndWordsFrom (start range) (toString $ slice range source) makeLeaf categories (range, substring) = cofree $ Info range categories 1 :< Leaf (T.pack substring) @@ -87,10 +87,10 @@ diffFiles parser renderer sourceBlobs = do -- | The sum of the node count of the diff’s patches. diffCostWithCachedTermSizes :: Diff a Info -> Integer -diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . headF . runCofree)) +diffCostWithCachedTermSizes = diffSum (getSum . foldMap (Sum . size . extract)) -- | The absolute difference between the node counts of a diff. diffCostWithAbsoluteDifferenceOfCachedDiffSizes :: Diff a Info -> Integer diffCostWithAbsoluteDifferenceOfCachedDiffSizes term = case runFree term of (Free (Both (before, after) :< _)) -> abs $ size before - size after - (Pure patch) -> sum $ size . headF . runCofree <$> patch + (Pure patch) -> sum $ size . extract <$> patch From b3842692cd79da93e7a50bb848b70d2ee4bb2b5b Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 26 May 2016 14:56:38 -0400 Subject: [PATCH 53/53] s/return/pure --- src/TreeSitter.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/TreeSitter.hs b/src/TreeSitter.hs index dd1abec45..e0313d0c7 100644 --- a/src/TreeSitter.hs +++ b/src/TreeSitter.hs @@ -54,11 +54,11 @@ documentToTerm constructor document contents = alloca $ \ root -> do name <- ts_node_p_name node document name <- peekCString name count <- ts_node_p_named_child_count node - children <- mapM (alloca . getChild node) $ take (fromIntegral count) [0..] + children <- traverse (alloca . getChild node) $ take (fromIntegral count) [0..] -- Note: The strict application here is semantically important. Without it, we may not evaluate the range until after we’ve exited the scope that `node` was allocated within, meaning `alloca` will free it & other stack data may overwrite it. - range <- return $! Range { start = fromIntegral $ ts_node_p_start_char node, end = fromIntegral $ ts_node_p_end_char node } + range <- pure $! Range { start = fromIntegral $ ts_node_p_start_char node, end = fromIntegral $ ts_node_p_end_char node } - return $! constructor contents range name children + pure $! constructor contents range name children getChild node n out = do _ <- ts_node_p_named_child node n out toTerm out