From 7169a512651720c75518c555e0fae9943e64d247 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 13 Jul 2016 11:58:43 -0400 Subject: [PATCH 01/26] Use wl-pprint-text to pretty print diff summaries --- semantic-diff.cabal | 2 ++ src/DiffSummary.hs | 27 ++++++++++++++------------- src/Renderer/Summary.hs | 3 ++- test/DiffSummarySpec.hs | 5 +++-- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index a1c619429..a01e2bc36 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -76,6 +76,7 @@ library , free , comonad , protolude + , wl-pprint-text default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards, LambdaCase ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j @@ -126,6 +127,7 @@ test-suite semantic-diff-test , these , free , recursion-schemes >= 4.1 + , wl-pprint-text if os(darwin) ghc-options: -threaded -rtsopts -with-rtsopts=-N -j else diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 2efbe689f..eae4c3188 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -12,7 +12,9 @@ import Category import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.OrderedMap -import Data.Text as Text (intercalate, unpack) +import Data.Text as Text (intercalate) +import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) +import qualified Text.PrettyPrint.Leijen.Text as P data DiffInfo = DiffInfo { categoryName :: Text, termName :: Text } deriving (Eq, Show) @@ -104,19 +106,18 @@ instance HasCategory leaf => HasCategory (Term leaf Info) where data DiffSummary a = DiffSummary { patch :: Patch a, parentAnnotations :: [Category] -} deriving (Eq, Functor) +} deriving (Eq, Functor, Show) -instance Show (DiffSummary DiffInfo) where - showsPrec _ DiffSummary{..} s = (++s) . unpack $ case patch of - (Insert diffInfo) -> "Added the " <> "'" <> termName diffInfo <> "' " <> categoryName diffInfo <> maybeParentContext parentAnnotations - (Delete diffInfo) -> "Deleted the " <> "'" <> termName diffInfo <> "' " <> categoryName diffInfo <> maybeParentContext parentAnnotations - (Replace t1 t2) -> - "Replaced the " <> "'" <> termName t1 <> "' " <> categoryName t1 - <> " with the " <> "'" <> termName t2 <> "' " <> categoryName t2 - <> maybeParentContext parentAnnotations - where maybeParentContext parentAnnotations = if null parentAnnotations - then "" - else " in the " <> intercalate "/" (toCategoryName <$> parentAnnotations) <> " context" +instance P.Pretty (DiffSummary DiffInfo) where + pretty DiffSummary{..} = case patch of + Insert diffInfo -> "Added the" <+> squotes (toDoc $ termName diffInfo) <+> (toDoc $ categoryName diffInfo) P.<> maybeParentContext parentAnnotations + Delete diffInfo -> "Deleted the" <+> squotes (toDoc $ termName diffInfo) <+> (toDoc $ categoryName diffInfo) P.<> maybeParentContext parentAnnotations + Replace t1 t2 -> "Replaced the" <+> squotes (toDoc $ termName t1) <+> (toDoc $ categoryName t1) <+> "with the" <+> P.squotes (toDoc $ termName t2) <+> (toDoc $ categoryName t2) P.<> maybeParentContext parentAnnotations + where + maybeParentContext annotations = if null annotations + then "" + else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" + toDoc = string . toS diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo] diffSummary = cata $ \case diff --git a/src/Renderer/Summary.hs b/src/Renderer/Summary.hs index feeb4fe39..4536e9d2c 100644 --- a/src/Renderer/Summary.hs +++ b/src/Renderer/Summary.hs @@ -5,6 +5,7 @@ import Renderer import DiffSummary import Data.Aeson import Data.Text (pack) +import Text.PrettyPrint.Leijen.Text (pretty) summary :: Renderer -summary diff _ = toS . encode $ pack . show <$> diffSummary diff +summary diff _ = toS . encode $ pack . show . pretty <$> diffSummary diff diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index f7db5b1eb..ef4b7f3a2 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -10,6 +10,7 @@ import Patch import Range import Category import DiffSummary +import Text.PrettyPrint.Leijen.Text arrayInfo :: Info arrayInfo = rangeAt 0 .: ArrayLiteral .: 2 .: 0 .: RNil @@ -33,6 +34,6 @@ spec = parallel $ do diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [ ArrayLiteral ] } ] describe "show" $ do it "should print adds" $ - show testSummary `shouldBe` ("Added the 'a' string" :: Text) + show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) it "prints a replacement" $ do - show replacementSummary `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) + show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) From 59426b86f6ab1896242a54ff021de37e1716b618 Mon Sep 17 00:00:00 2001 From: joshvera Date: Wed, 13 Jul 2016 14:32:53 -0400 Subject: [PATCH 02/26] Add megaparsec to pretty print summaries --- semantic-diff.cabal | 1 + src/Category.hs | 26 +++++++++++++++++++++++--- src/Data/Record.hs | 1 + src/DiffSummary.hs | 8 +++++++- test/AlignmentSpec.hs | 1 + test/CorpusSpec.hs | 4 ++-- test/DiffSummarySpec.hs | 27 ++++++++++++++++++++++++++- 7 files changed, 61 insertions(+), 7 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index a01e2bc36..0fdaded56 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -128,6 +128,7 @@ test-suite semantic-diff-test , free , recursion-schemes >= 4.1 , wl-pprint-text + , megaparsec if os(darwin) ghc-options: -threaded -rtsopts -with-rtsopts=-N -j else diff --git a/src/Category.hs b/src/Category.hs index e904efb92..9e9bad4eb 100644 --- a/src/Category.hs +++ b/src/Category.hs @@ -2,7 +2,7 @@ module Category where import Prologue import Data.Hashable -import Test.QuickCheck +import Test.QuickCheck (oneof, Arbitrary, arbitrary, shrink) import Data.Text.Arbitrary() -- | A standardized category of AST node. Used to determine the semantics for @@ -78,17 +78,37 @@ data Category instance Hashable Category instance Arbitrary Category where - arbitrary = oneof - [ pure Program + arbitrary = oneof [ + pure Program , pure Error + , pure Boolean , pure BinaryOperator , pure DictionaryLiteral , pure Pair , pure FunctionCall + , pure Function + , pure Identifier + , pure Params + , pure ExpressionStatements + , pure MethodCall + , pure Args , pure StringLiteral , pure IntegerLiteral + , pure Regex , pure SymbolLiteral + , pure TemplateString , pure ArrayLiteral + , pure Assignment + , pure MathAssignment + , pure MemberAccess + , pure SubscriptAccess + , pure VarAssignment + , pure VarDecl + , pure Switch + , pure Ternary + , pure Case + , pure Operator + , pure Object , Other <$> arbitrary ] diff --git a/src/Data/Record.hs b/src/Data/Record.hs index 5d6043771..8cb72136e 100644 --- a/src/Data/Record.hs +++ b/src/Data/Record.hs @@ -3,6 +3,7 @@ module Data.Record where import Prologue import Test.QuickCheck +import GHC.Show (Show(..)) -- | A type-safe, extensible record structure. -- | diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index eae4c3188..84b1f72ee 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -13,6 +13,8 @@ import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.OrderedMap import Data.Text as Text (intercalate) +import Test.QuickCheck hiding (Fixed) +import Patch.Arbitrary() import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) import qualified Text.PrettyPrint.Leijen.Text as P @@ -106,7 +108,11 @@ instance HasCategory leaf => HasCategory (Term leaf Info) where data DiffSummary a = DiffSummary { patch :: Patch a, parentAnnotations :: [Category] -} deriving (Eq, Functor, Show) +} deriving (Eq, Functor, Show, Generic) + +instance (Eq a, Arbitrary a) => Arbitrary (DiffSummary a) where + arbitrary = DiffSummary <$> arbitrary <*> arbitrary + shrink = genericShrink instance P.Pretty (DiffSummary DiffInfo) where pretty DiffSummary{..} = case patch of diff --git a/test/AlignmentSpec.hs b/test/AlignmentSpec.hs index 13730d8a2..e06474553 100644 --- a/test/AlignmentSpec.hs +++ b/test/AlignmentSpec.hs @@ -27,6 +27,7 @@ import Term import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck +import GHC.Show (Show(..)) spec :: Spec spec = parallel $ do diff --git a/test/CorpusSpec.hs b/test/CorpusSpec.hs index 57459ac38..84425de6f 100644 --- a/test/CorpusSpec.hs +++ b/test/CorpusSpec.hs @@ -1,7 +1,6 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} module CorpusSpec where -import System.IO import Data.String import Diffing import Renderer @@ -20,6 +19,7 @@ import qualified Source as S import System.FilePath import System.FilePath.Glob import Test.Hspec +import GHC.Show (Show(..)) spec :: Spec spec = parallel $ do @@ -76,7 +76,7 @@ testDiff renderer paths diff matcher = do case diff of Nothing -> matcher actual actual Just file -> do - expected <- Verbatim . T.pack <$> readFile file + expected <- Verbatim <$> readFile file matcher actual expected where parser = parserForFilepath (fst paths) sourceBlobs sources = pure S.SourceBlob <*> sources <*> pure mempty <*> paths <*> pure (Just S.defaultPlainBlob) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index ef4b7f3a2..977652641 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} module DiffSummarySpec where import Prologue @@ -10,7 +11,12 @@ import Patch import Range import Category import DiffSummary -import Text.PrettyPrint.Leijen.Text +import Text.PrettyPrint.Leijen.Text (pretty) +import Test.Hspec.QuickCheck +import Interpreter +import Term.Arbitrary +import Text.Megaparsec.Text +import Text.Megaparsec arrayInfo :: Info arrayInfo = rangeAt 0 .: ArrayLiteral .: 2 .: 0 .: RNil @@ -37,3 +43,22 @@ spec = parallel $ do show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) it "prints a replacement" $ do show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) + prop "diff summaries of arbitrary diffs are identical" $ + \a b -> let + diff = diffTerms wrap (==) diffCost (toTerm a) (toTerm (b :: ArbitraryTerm Text Info)) + prettyDiff = show . pretty $ diffSummary diff in + parsePrettyDiff prettyDiff `shouldBe` Just (diffSummary diff) + +parsePrettyDiff :: Text -> Maybe [DiffSummary DiffInfo] +parsePrettyDiff string = parseMaybe diffParser string + +parsePatch :: Parsec Text (Patch Text) +parsePatch = (\x y z -> case x of + "Added" -> Insert (toS z) + "Deleted" -> Delete (toS z)) <$> (string "Added" <|> string "Deleted") <*> (space *> string "the" <* space) <*> between (char '\'') (char '\'') (many printChar) + +diffParser :: Parsec Text (DiffSummary DiffInfo) +diffParser = do + patch <- parsePatch + annotations <- _ + pure $ DiffSummary patch annotations \ No newline at end of file From 2ebbe72a76900e06ec1043dfd8977ccd5264db6f Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 14 Jul 2016 11:52:03 -0400 Subject: [PATCH 03/26] Add Categorizable newtype for HasCategory (Record fields) --- src/DiffSummary.hs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 84b1f72ee..30e60476e 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -4,9 +4,9 @@ module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where import Prologue hiding (snd, intercalate) import Diff -import Info (Info, category) import Patch import Term +import Info (category) import Syntax import Category import Data.Functor.Foldable as Foldable @@ -15,12 +15,13 @@ import Data.OrderedMap import Data.Text as Text (intercalate) import Test.QuickCheck hiding (Fixed) import Patch.Arbitrary() +import Data.Record import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) import qualified Text.PrettyPrint.Leijen.Text as P data DiffInfo = DiffInfo { categoryName :: Text, termName :: Text } deriving (Eq, Show) -toTermName :: HasCategory leaf => Term leaf Info -> Text +toTermName :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> Text toTermName term = case unwrap term of Fixed children -> fromMaybe "EmptyFixedNode" $ (toCategoryName . category) . extract <$> head children Indexed children -> fromMaybe "EmptyIndexedNode" $ (toCategoryName . category) . extract <$> head children @@ -65,8 +66,9 @@ class HasCategory a where instance HasCategory Text where toCategoryName = identity -instance HasCategory Info where - toCategoryName = toCategoryName . category +newtype Categorizable a = Categorizable a +instance (HasField fields Category) => HasCategory (Categorizable (Record fields)) where + toCategoryName (Categorizable a)= toCategoryName $ category a instance HasCategory Category where toCategoryName = \case @@ -102,7 +104,7 @@ instance HasCategory Category where TemplateString -> "template string" Category.Object -> "object" -instance HasCategory leaf => HasCategory (Term leaf Info) where +instance (HasCategory leaf, HasField fields Category) => HasCategory (Term leaf (Record fields)) where toCategoryName = toCategoryName . category . extract data DiffSummary a = DiffSummary { @@ -125,7 +127,7 @@ instance P.Pretty (DiffSummary DiffInfo) where else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" toDoc = string . toS -diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo] +diffSummary :: (HasCategory leaf, HasField fields Category) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] diffSummary = cata $ \case -- Skip comments and leaves since they don't have any changes (Free (_ :< Leaf _)) -> [] @@ -154,23 +156,23 @@ diffSummary = cata $ \case (Pure (Delete term)) -> (\info -> DiffSummary (Delete info) []) <$> termToDiffInfo term (Pure (Replace t1 t2)) -> (\(info1, info2) -> DiffSummary (Replace info1 info2) []) <$> zip (termToDiffInfo t1) (termToDiffInfo t2) -termToDiffInfo :: HasCategory leaf => Term leaf Info -> [DiffInfo] +termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> [DiffInfo] termToDiffInfo term = case runCofree term of (_ :< Leaf _) -> [ DiffInfo (toCategoryName term) (toTermName term) ] (_ :< Indexed children) -> join $ termToDiffInfo <$> children (_ :< Fixed children) -> join $ termToDiffInfo <$> children (_ :< Keyed children) -> join $ termToDiffInfo <$> Prologue.toList children - (info :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName info) (toTermName identifier) ] - (info :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName info) (toTermName ternaryCondition) ] - (info :< Syntax.Function identifier _ _) -> [ DiffInfo (toCategoryName info) (maybe "anonymous" toTermName identifier) ] - (info :< Syntax.Assignment identifier _) -> [ DiffInfo (toCategoryName info) (toTermName identifier) ] - (info :< Syntax.MathAssignment identifier _) -> [ DiffInfo (toCategoryName info) (toTermName identifier) ] + (info :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName identifier) ] + (info :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName ternaryCondition) ] + (info :< Syntax.Function identifier _ _) -> [ DiffInfo (toCategoryName $ Categorizable info) (maybe "anonymous" toTermName identifier) ] + (info :< Syntax.Assignment identifier _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName identifier) ] + (info :< Syntax.MathAssignment identifier _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName identifier) ] -- Currently we cannot express the operator for an operator production from TreeSitter. Eventually we should be able to -- use the term name of the operator identifier when we have that production value. Until then, I'm using a placeholder value -- to indicate where that value should be when constructing DiffInfos. - (info :< Syntax.Operator _) -> [DiffInfo (toCategoryName info) "x"] - (info :< Commented cs leaf) -> join (termToDiffInfo <$> cs) <> maybe [] (\leaf -> [ DiffInfo (toCategoryName info) (toTermName leaf) ]) leaf - (info :< _) -> [ DiffInfo (toCategoryName info) (toTermName term) ] + (info :< Syntax.Operator _) -> [DiffInfo (toCategoryName $ Categorizable info) "x"] + (info :< Commented cs leaf) -> join (termToDiffInfo <$> cs) <> maybe [] (\leaf -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName leaf) ]) leaf + (info :< _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName term) ] prependSummary :: Category -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } From 922e845c786aa7054e97686663c13f2c01103f17 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 14 Jul 2016 11:52:40 -0400 Subject: [PATCH 04/26] Add property test to test diff summary constructors --- test/DiffSummarySpec.hs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 977652641..107a69b58 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -14,7 +14,7 @@ import DiffSummary import Text.PrettyPrint.Leijen.Text (pretty) import Test.Hspec.QuickCheck import Interpreter -import Term.Arbitrary +import Diff.Arbitrary import Text.Megaparsec.Text import Text.Megaparsec @@ -44,21 +44,18 @@ spec = parallel $ do it "prints a replacement" $ do show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) prop "diff summaries of arbitrary diffs are identical" $ - \a b -> let - diff = diffTerms wrap (==) diffCost (toTerm a) (toTerm (b :: ArbitraryTerm Text Info)) - prettyDiff = show . pretty $ diffSummary diff in - parsePrettyDiff prettyDiff `shouldBe` Just (diffSummary diff) + \a -> let + diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category]))) + summaries = diffSummary diff in + ((() <$) . patch <$> summaries) `shouldBe` ((() <$) <$> toList diff) parsePrettyDiff :: Text -> Maybe [DiffSummary DiffInfo] parsePrettyDiff string = parseMaybe diffParser string -parsePatch :: Parsec Text (Patch Text) -parsePatch = (\x y z -> case x of - "Added" -> Insert (toS z) - "Deleted" -> Delete (toS z)) <$> (string "Added" <|> string "Deleted") <*> (space *> string "the" <* space) <*> between (char '\'') (char '\'') (many printChar) +parsePatch :: Parsec Text (Patch DiffInfo) +parsePatch = (\x y z a -> case x of + "Added" -> Insert (DiffInfo (toS z) (toS a)) + "Deleted" -> Delete (DiffInfo(toS z) (toS a))) <$> (string "Added" <|> string "Deleted") <*> (space *> string "the" <* space) <*> between (char '\'') (char '\'') (many printChar) <*> (space *> many printChar) -diffParser :: Parsec Text (DiffSummary DiffInfo) -diffParser = do - patch <- parsePatch - annotations <- _ - pure $ DiffSummary patch annotations \ No newline at end of file +diffParser :: Parsec Text [(DiffSummary DiffInfo)] +diffParser = (DiffSummary <$> parsePatch <*> pure []) `sepBy` newline From a9adce26104f717e6addd66b4d492efc27f152f7 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 14 Jul 2016 13:01:16 -0400 Subject: [PATCH 05/26] other patches should be the same as summary patches --- test/DiffSummarySpec.hs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 107a69b58..6ef7fb313 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -7,6 +7,7 @@ import Test.Hspec import Diff import Info import Syntax +import Term import Patch import Range import Category @@ -17,6 +18,7 @@ import Interpreter import Diff.Arbitrary import Text.Megaparsec.Text import Text.Megaparsec +import Data.List (partition) arrayInfo :: Info arrayInfo = rangeAt 0 .: ArrayLiteral .: 2 .: 0 .: RNil @@ -46,8 +48,28 @@ spec = parallel $ do prop "diff summaries of arbitrary diffs are identical" $ \a -> let diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category]))) - summaries = diffSummary diff in - ((() <$) . patch <$> summaries) `shouldBe` ((() <$) <$> toList diff) + summaries = diffSummary diff + patches = toList diff + isIndexedOrFixed :: Patch (Term a annotation) -> Bool + isIndexedOrFixed patch = case unwrap <$> patch of + (Insert syntax) -> isIndexedOrFixed' syntax + (Delete syntax) -> isIndexedOrFixed' syntax + (Replace s1 s2) -> isIndexedOrFixed' s1 || isIndexedOrFixed' s2 + isIndexedOrFixed' = \case + (Indexed _) -> True + (Fixed _) -> True + _ -> False + isBranchCategory = \case + (Insert info) -> categoryName info `elem` ["Indexed", "Fixed"] + (Delete info) -> categoryName info `elem` ["Indexed", "Fixed"] + (Replace i1 i2) -> categoryName i1 `elem` ["Indexed", "Fixed"] || categoryName i2 `elem` ["Indexed", "Fixed"] + in + case (partition isIndexedOrFixed patches, partition isBranchCategory (patch <$> summaries)) of + ((branchPatches, otherPatches), (branchSummaries, otherSummaries)) -> + (() <$ branchPatches, () <$ otherPatches) `shouldBe` (() <$ branchSummaries, () <$ otherSummaries) + + -- ((() <$) . patch <$> summaries) `shouldBe` ((() <$) <$> otherPatches) + parsePrettyDiff :: Text -> Maybe [DiffSummary DiffInfo] parsePrettyDiff string = parseMaybe diffParser string From 1c65d03618b9a9b7c61c79f03f3050887256c17c Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 14 Jul 2016 13:01:33 -0400 Subject: [PATCH 06/26] Output a diff summary for empty branch syntax nodes --- src/DiffSummary.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 30e60476e..5b0c429cc 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -159,8 +159,8 @@ diffSummary = cata $ \case termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> [DiffInfo] termToDiffInfo term = case runCofree term of (_ :< Leaf _) -> [ DiffInfo (toCategoryName term) (toTermName term) ] - (_ :< Indexed children) -> join $ termToDiffInfo <$> children - (_ :< Fixed children) -> join $ termToDiffInfo <$> children + (info :< Indexed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children + (info :< Fixed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children (_ :< Keyed children) -> join $ termToDiffInfo <$> Prologue.toList children (info :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName identifier) ] (info :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName ternaryCondition) ] From 13671e5d14d2e87afc9c942a1eb564b36e5f7982 Mon Sep 17 00:00:00 2001 From: joshvera Date: Thu, 14 Jul 2016 19:35:23 -0400 Subject: [PATCH 07/26] Add patchAnnotations to DiffInfo to track discarded branch nodes --- src/DiffSummary.hs | 64 ++++++++++++++++++++++++++++++++--------- src/Diffing.hs | 16 +++++++---- test/DiffSummarySpec.hs | 41 +++++++++++++------------- 3 files changed, 82 insertions(+), 39 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index c41d88841..9e97bce0f 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -6,9 +6,13 @@ import Prologue hiding (snd, intercalate) import Diff import Patch import Term -import Info (category) +import Info (category, Cost) import Syntax import Category +import Interpreter (diffTerms) +import Diffing +import Data.Align +import Data.These import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.Text as Text (intercalate) @@ -17,13 +21,14 @@ import Patch.Arbitrary() import Data.Record import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) import qualified Text.PrettyPrint.Leijen.Text as P +import Data.Hashable data DiffInfo = DiffInfo { categoryName :: Text, termName :: Text } deriving (Eq, Show) toTermName :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> Text toTermName term = case unwrap term of - Fixed children -> fromMaybe "branch" $ (toCategoryName . category) . extract <$> head children - Indexed children -> fromMaybe "branch" $ (toCategoryName . category) . extract <$> head children + Syntax.Fixed children -> fromMaybe "branch" $ (toCategoryName . category) . extract <$> head children + Syntax.Indexed children -> fromMaybe "branch" $ (toCategoryName . category) . extract <$> head children Leaf leaf -> toCategoryName leaf Syntax.Assignment identifier value -> toTermName identifier <> toTermName value Syntax.Function identifier _ _ -> (maybe "anonymous" toTermName identifier) @@ -105,13 +110,19 @@ instance HasCategory Category where instance (HasCategory leaf, HasField fields Category) => HasCategory (Term leaf (Record fields)) where toCategoryName = toCategoryName . category . extract +data Branch = Indexed | Fixed deriving (Show, Eq, Generic) +instance Arbitrary Branch where + arbitrary = oneof [ pure DiffSummary.Indexed, pure DiffSummary.Fixed ] + shrink = genericShrink + data DiffSummary a = DiffSummary { patch :: Patch a, - parentAnnotations :: [Category] + parentAnnotations :: [Category], + patchAnnotations :: [Patch Branch] } deriving (Eq, Functor, Show, Generic) instance (Eq a, Arbitrary a) => Arbitrary (DiffSummary a) where - arbitrary = DiffSummary <$> arbitrary <*> arbitrary + arbitrary = DiffSummary <$> arbitrary <*> arbitrary <*> arbitrary shrink = genericShrink instance P.Pretty (DiffSummary DiffInfo) where @@ -125,13 +136,13 @@ instance P.Pretty (DiffSummary DiffInfo) where else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" toDoc = string . toS -diffSummary :: (HasCategory leaf, HasField fields Category) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] +diffSummary :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] diffSummary = cata $ \case -- Skip comments and leaves since they don't have any changes (Free (_ :< Leaf _)) -> [] Free (_ :< (Syntax.Comment _)) -> [] - (Free (infos :< Indexed children)) -> prependSummary (category $ snd infos) <$> join children - (Free (infos :< Fixed children)) -> prependSummary (category $ snd infos) <$> join children + (Free (infos :< Syntax.Indexed children)) -> prependSummary (category $ snd infos) <$> join children + (Free (infos :< Syntax.Fixed children)) -> prependSummary (category $ snd infos) <$> join children (Free (infos :< Syntax.FunctionCall identifier children)) -> prependSummary (category $ snd infos) <$> join (Prologue.toList (identifier : children)) (Free (infos :< Syntax.Function id ps body)) -> prependSummary (category $ snd infos) <$> (fromMaybe [] id) <> (fromMaybe [] ps) <> body (Free (infos :< Syntax.Assignment id value)) -> prependSummary (category $ snd infos) <$> id <> value @@ -149,15 +160,42 @@ diffSummary = cata $ \case Free (infos :< (Syntax.Object kvs)) -> prependSummary (category $ snd infos) <$> join kvs Free (infos :< (Syntax.Pair a b)) -> prependSummary (category $ snd infos) <$> a <> b Free (infos :< (Syntax.Commented cs leaf)) -> prependSummary (category $ snd infos) <$> join cs <> fromMaybe [] leaf - (Pure (Insert term)) -> (\info -> DiffSummary (Insert info) []) <$> termToDiffInfo term - (Pure (Delete term)) -> (\info -> DiffSummary (Delete info) []) <$> termToDiffInfo term - (Pure (Replace t1 t2)) -> (\(info1, info2) -> DiffSummary (Replace info1 info2) []) <$> zip (termToDiffInfo t1) (termToDiffInfo t2) + (Pure (Insert term)) -> (\info -> DiffSummary (Insert info) [] []) <$> termToDiffInfo term + (Pure (Insert term)) -> case unwrap term of + (Syntax.Indexed terms) -> + (\info -> DiffSummary (Insert info) [] [Delete DiffSummary.Indexed]) <$> (join $ termToDiffInfo <$> terms) + (Syntax.Fixed terms) -> + (\info -> DiffSummary (Insert info) [] [Delete DiffSummary.Fixed]) <$> (join $ termToDiffInfo <$> terms) + (Pure (Delete term)) -> case unwrap term of + (Syntax.Indexed terms) -> + (\info -> DiffSummary (Delete info) [] [Delete DiffSummary.Indexed]) <$> termToDiffInfo term + (Syntax.Fixed terms) -> + (\info -> DiffSummary (Delete info) [] [Delete DiffSummary.Fixed]) <$> termToDiffInfo term + (Pure (Replace t1 t2)) -> case (unwrap t1, unwrap t2) of + (Syntax.Indexed t1', Syntax.Indexed t2') -> + (\patch -> DiffSummary patch [] [Replace DiffSummary.Indexed DiffSummary.Indexed]) <$> join (alignWith summarizeThese t1' t2') + (Syntax.Indexed t1', Syntax.Fixed t2') -> + (\patch -> DiffSummary patch [] [Replace DiffSummary.Indexed DiffSummary.Fixed]) <$> join (alignWith summarizeThese t1' t2') + (Syntax.Fixed t1', Syntax.Indexed t2') -> + (\patch -> DiffSummary patch [] [Replace DiffSummary.Fixed DiffSummary.Indexed]) <$> join (alignWith summarizeThese t1' t2') + (Syntax.Fixed t1', Syntax.Fixed t2') -> + (\patch -> DiffSummary patch [] [Replace DiffSummary.Fixed DiffSummary.Fixed]) <$> join (alignWith summarizeThese t1' t2') + (_, _) -> + (\(info1, info2) -> DiffSummary (Replace info1 info2) [] []) <$> zip (termToDiffInfo t1) (termToDiffInfo t2) + +summarizeThese :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => These (Term leaf (Record fields)) (Term leaf (Record fields)) -> [Patch DiffInfo] +summarizeThese = these (sequence . Delete <$> termToDiffInfo) (sequence . Insert <$> termToDiffInfo) (\t1 t2 -> patch <$> summaries t1 t2) + where + summaries :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => Term leaf (Record fields) -> Term leaf (Record fields) -> [DiffSummary DiffInfo] + summaries t1 t2 = diffSummary $ diffTerms construct compareCategoryEq diffCostWithCachedTermSizes t1 t2 termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> [DiffInfo] termToDiffInfo term = case runCofree term of (_ :< Leaf _) -> [ DiffInfo (toCategoryName term) (toTermName term) ] - (info :< Indexed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children - (info :< Fixed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children + (info :< Syntax.Indexed children) -> if null children + then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] + else join $ termToDiffInfo <$> children + (info :< Syntax.Fixed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children (info :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName identifier) ] (info :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName ternaryCondition) ] (info :< Syntax.Function identifier _ _) -> [ DiffInfo (toCategoryName $ Categorizable info) (maybe "anonymous" toTermName identifier) ] diff --git a/src/Diffing.hs b/src/Diffing.hs index cc813c00f..8d047b40e 100644 --- a/src/Diffing.hs +++ b/src/Diffing.hs @@ -92,19 +92,23 @@ diffFiles parser renderer sourceBlobs = do (True, False) -> pure $ Insert (snd terms) (False, True) -> pure $ Delete (fst terms) (_, _) -> - runBothWith (diffTerms construct shouldCompareTerms diffCostWithCachedTermSizes) $ replaceLeaves <*> terms + runBothWith (diffTerms construct compareCategoryEq diffCostWithCachedTermSizes) $ replaceLeaves <*> terms pure $! renderer textDiff sourceBlobs - where construct :: CofreeF (Syntax Text) (Both Info) (Diff Text Info) -> Diff Text Info - construct (info :< syntax) = free (Free ((setCost <$> info <*> sumCost syntax) :< syntax)) - sumCost = fmap getSum . foldMap (fmap Sum . getCost) + + +construct :: HasField fields Cost => CofreeF (Syntax leaf) (Both (Record fields)) (Diff leaf (Record fields)) -> Diff leaf (Record fields) +construct (info :< syntax) = free (Free ((setCost <$> info <*> sumCost syntax) :< syntax)) + where sumCost = fmap getSum . foldMap (fmap Sum . getCost) getCost diff = case runFree diff of Free (info :< _) -> cost <$> info Pure patch -> uncurry both (fromThese 0 0 (unPatch (cost . extract <$> patch))) - shouldCompareTerms = (==) `on` category . extract + +compareCategoryEq :: HasField fields Category => Term leaf (Record fields) -> Term leaf (Record fields) -> Bool +compareCategoryEq = (==) `on` category . extract -- | The sum of the node count of the diff’s patches. -diffCostWithCachedTermSizes :: Diff a Info -> Integer +diffCostWithCachedTermSizes :: HasField fields Cost => Diff a (Record fields) -> Integer diffCostWithCachedTermSizes diff = unCost $ case runFree diff of Free (info :< _) -> sum (cost <$> info) Pure patch -> sum (cost . extract <$> patch) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 1b0d4ff23..01b776db4 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -30,24 +30,24 @@ testDiff :: Diff Text Info testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) testSummary :: DiffSummary DiffInfo -testSummary = DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [] } +testSummary = DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [], patchAnnotations = [] } replacementSummary :: DiffSummary DiffInfo -replacementSummary = DiffSummary { patch = Replace (DiffInfo "string" "a") (DiffInfo "symbol" "b"), parentAnnotations = [ ArrayLiteral ] } +replacementSummary = DiffSummary { patch = Replace (DiffInfo "string" "a") (DiffInfo "symbol" "b"), parentAnnotations = [ ArrayLiteral ], patchAnnotations = [] } spec :: Spec spec = parallel $ do describe "diffSummary" $ do it "outputs a diff summary" $ do - diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [ ArrayLiteral ] } ] + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [ ArrayLiteral ], patchAnnotations = [] } ] describe "show" $ do it "should print adds" $ show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) it "prints a replacement" $ do show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) - prop "diff summaries of arbitrary diffs are identical" $ + prop "patches in summaries match the patches in diffs" $ \a -> let - diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category]))) + diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category, Cost]))) summaries = diffSummary diff patches = toList diff isIndexedOrFixed :: Patch (Term a annotation) -> Bool @@ -59,22 +59,23 @@ spec = parallel $ do (Indexed _) -> True (Fixed _) -> True _ -> False - isBranchCategory syntax = case syntax of; (Insert info) -> termName info == "branch" || categoryName info `elem` ["Indexed", "Fixed"]; (Delete info) -> termName info == "branch" || categoryName info `elem` ["Indexed", "Fixed"]; (Replace i1 i2) -> termName i1 == "branch" || categoryName i1 `elem` ["Indexed", "Fixed"] || categoryName i1 `elem` ["Indexed", "Fixed"] || termName i2 == "branch"; + isBranchNode :: DiffSummary DiffInfo -> Bool + isBranchNode summary = (not . null $ patchAnnotations summary) || (case patch summary of + (Insert diffInfo) -> termName diffInfo == "branch" + (Delete diffInfo) -> termName diffInfo == "branch" + (Replace i1 i2) -> termName i1 == "branch" || termName i2 == "branch") in - case (partition isBranchCategory (patch <$> summaries), partition isIndexedOrFixed patches) of + case (partition isBranchNode summaries, partition isIndexedOrFixed patches) of ((branchSummaries, otherSummaries), (branchPatches, otherPatches)) -> - (() <$ branchSummaries, () <$ otherSummaries) `shouldBe` (() <$ branchPatches, () <$ otherPatches) + ((() <$) . patch <$> branchSummaries, (() <$) . patch <$> otherSummaries) `shouldBe` ((() <$) <$> branchPatches, (() <$) <$> otherPatches) + -- ((() <$) <$> (patch <$> summaries)) `shouldBe` ((() <$) <$> patches) + -- [Insert (), Insert ()] == [ Insert () ] + -- explodePatch :: Patch (Syntax a) -> [Patch (Syntax a)] + -- explodePatch Indexed = explodePatch <$> children + + -- Patches of branch nodes with children should have a summary for each child that is not a branch node + -- Patches of branch nodes with children that are branch nodes shoudl have a summary for each of those children or one summary per branch if the branches are empty +-- let xs = ArbitraryPure (Insert (ArbitraryTerm {annotation = Category.Operator .: RNil, syntax = Indexed [ArbitraryTerm {annotation = Program .: RNil, syntax = Leaf ""}]})) +-- let xs = ArbitraryPure (Delete (ArbitraryTerm {annotation = Category.Case .: RNil, syntax = Fixed [ArbitraryTerm {annotation = Category.FunctionCall .: RNil, syntax = Leaf ""}]})) :: ArbitraryDiff Text (Record '[Category]) -- ((() <$) . patch <$> summaries) `shouldBe` ((() <$) <$> otherPatches) - - -parsePrettyDiff :: Text -> Maybe [DiffSummary DiffInfo] -parsePrettyDiff string = parseMaybe diffParser string - -parsePatch :: Parsec Text (Patch DiffInfo) -parsePatch = (\x y z a -> case x of - "Added" -> Insert (DiffInfo (toS z) (toS a)) - "Deleted" -> Delete (DiffInfo(toS z) (toS a))) <$> (string "Added" <|> string "Deleted") <*> (space *> string "the" <* space) <*> between (char '\'') (char '\'') (many printChar) <*> (space *> many printChar) - -diffParser :: Parsec Text [(DiffSummary DiffInfo)] -diffParser = (DiffSummary <$> parsePatch <*> pure []) `sepBy` newline From e57f30f20bc80d255acac91bf5266ff83b18ef33 Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 13:18:45 -0400 Subject: [PATCH 08/26] DiffInfo are now either LeafInfos or BranchInfos --- src/DiffSummary.hs | 74 ++++++++++++----------------------------- test/DiffSummarySpec.hs | 31 ++++++----------- 2 files changed, 32 insertions(+), 73 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 9e97bce0f..97e39ee47 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -9,10 +9,6 @@ import Term import Info (category, Cost) import Syntax import Category -import Interpreter (diffTerms) -import Diffing -import Data.Align -import Data.These import Data.Functor.Foldable as Foldable import Data.Functor.Both import Data.Text as Text (intercalate) @@ -23,7 +19,9 @@ import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) import qualified Text.PrettyPrint.Leijen.Text as P import Data.Hashable -data DiffInfo = DiffInfo { categoryName :: Text, termName :: Text } deriving (Eq, Show) +data DiffInfo = LeafInfo { categoryName :: Text, termName :: Text } + | BranchInfo { branches :: [ DiffInfo ], categoryName :: Text, branchType :: Branch } + deriving (Eq, Show) toTermName :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> Text toTermName term = case unwrap term of @@ -110,19 +108,18 @@ instance HasCategory Category where instance (HasCategory leaf, HasField fields Category) => HasCategory (Term leaf (Record fields)) where toCategoryName = toCategoryName . category . extract -data Branch = Indexed | Fixed deriving (Show, Eq, Generic) +data Branch = BIndexed | BFixed | BCommented deriving (Show, Eq, Generic) instance Arbitrary Branch where - arbitrary = oneof [ pure DiffSummary.Indexed, pure DiffSummary.Fixed ] + arbitrary = oneof [ pure BIndexed, pure BFixed ] shrink = genericShrink data DiffSummary a = DiffSummary { patch :: Patch a, - parentAnnotations :: [Category], - patchAnnotations :: [Patch Branch] + parentAnnotations :: [Category] } deriving (Eq, Functor, Show, Generic) instance (Eq a, Arbitrary a) => Arbitrary (DiffSummary a) where - arbitrary = DiffSummary <$> arbitrary <*> arbitrary <*> arbitrary + arbitrary = DiffSummary <$> arbitrary <*> arbitrary shrink = genericShrink instance P.Pretty (DiffSummary DiffInfo) where @@ -160,53 +157,26 @@ diffSummary = cata $ \case Free (infos :< (Syntax.Object kvs)) -> prependSummary (category $ snd infos) <$> join kvs Free (infos :< (Syntax.Pair a b)) -> prependSummary (category $ snd infos) <$> a <> b Free (infos :< (Syntax.Commented cs leaf)) -> prependSummary (category $ snd infos) <$> join cs <> fromMaybe [] leaf - (Pure (Insert term)) -> (\info -> DiffSummary (Insert info) [] []) <$> termToDiffInfo term - (Pure (Insert term)) -> case unwrap term of - (Syntax.Indexed terms) -> - (\info -> DiffSummary (Insert info) [] [Delete DiffSummary.Indexed]) <$> (join $ termToDiffInfo <$> terms) - (Syntax.Fixed terms) -> - (\info -> DiffSummary (Insert info) [] [Delete DiffSummary.Fixed]) <$> (join $ termToDiffInfo <$> terms) - (Pure (Delete term)) -> case unwrap term of - (Syntax.Indexed terms) -> - (\info -> DiffSummary (Delete info) [] [Delete DiffSummary.Indexed]) <$> termToDiffInfo term - (Syntax.Fixed terms) -> - (\info -> DiffSummary (Delete info) [] [Delete DiffSummary.Fixed]) <$> termToDiffInfo term - (Pure (Replace t1 t2)) -> case (unwrap t1, unwrap t2) of - (Syntax.Indexed t1', Syntax.Indexed t2') -> - (\patch -> DiffSummary patch [] [Replace DiffSummary.Indexed DiffSummary.Indexed]) <$> join (alignWith summarizeThese t1' t2') - (Syntax.Indexed t1', Syntax.Fixed t2') -> - (\patch -> DiffSummary patch [] [Replace DiffSummary.Indexed DiffSummary.Fixed]) <$> join (alignWith summarizeThese t1' t2') - (Syntax.Fixed t1', Syntax.Indexed t2') -> - (\patch -> DiffSummary patch [] [Replace DiffSummary.Fixed DiffSummary.Indexed]) <$> join (alignWith summarizeThese t1' t2') - (Syntax.Fixed t1', Syntax.Fixed t2') -> - (\patch -> DiffSummary patch [] [Replace DiffSummary.Fixed DiffSummary.Fixed]) <$> join (alignWith summarizeThese t1' t2') - (_, _) -> - (\(info1, info2) -> DiffSummary (Replace info1 info2) [] []) <$> zip (termToDiffInfo t1) (termToDiffInfo t2) + (Pure (Insert term)) -> [ DiffSummary (Insert $ termToDiffInfo term) [] ] + (Pure (Delete term)) -> [ DiffSummary (Delete $ termToDiffInfo term) [] ] + (Pure (Replace t1 t2)) -> [ DiffSummary (Replace (termToDiffInfo t1) (termToDiffInfo t2)) [] ] -summarizeThese :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => These (Term leaf (Record fields)) (Term leaf (Record fields)) -> [Patch DiffInfo] -summarizeThese = these (sequence . Delete <$> termToDiffInfo) (sequence . Insert <$> termToDiffInfo) (\t1 t2 -> patch <$> summaries t1 t2) - where - summaries :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => Term leaf (Record fields) -> Term leaf (Record fields) -> [DiffSummary DiffInfo] - summaries t1 t2 = diffSummary $ diffTerms construct compareCategoryEq diffCostWithCachedTermSizes t1 t2 - -termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> [DiffInfo] +termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> DiffInfo termToDiffInfo term = case runCofree term of - (_ :< Leaf _) -> [ DiffInfo (toCategoryName term) (toTermName term) ] - (info :< Syntax.Indexed children) -> if null children - then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] - else join $ termToDiffInfo <$> children - (info :< Syntax.Fixed children) -> if null children then [ DiffInfo (toCategoryName (Categorizable info)) (toTermName term) ] else join $ termToDiffInfo <$> children - (info :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName identifier) ] - (info :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName (Categorizable info)) (toTermName ternaryCondition) ] - (info :< Syntax.Function identifier _ _) -> [ DiffInfo (toCategoryName $ Categorizable info) (maybe "anonymous" toTermName identifier) ] - (info :< Syntax.Assignment identifier _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName identifier) ] - (info :< Syntax.MathAssignment identifier _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName identifier) ] + (_ :< Leaf _) -> LeafInfo (toCategoryName term) (toTermName term) + (info :< Syntax.Indexed children) -> BranchInfo (termToDiffInfo <$> children) (toCategoryName (Categorizable info)) BIndexed + (info :< Syntax.Fixed children) -> BranchInfo (termToDiffInfo <$> children) (toCategoryName (Categorizable info)) BFixed + (info :< Syntax.FunctionCall identifier _) -> LeafInfo (toCategoryName (Categorizable info)) (toTermName identifier) + (info :< Syntax.Ternary ternaryCondition _) -> LeafInfo (toCategoryName (Categorizable info)) (toTermName ternaryCondition) + (info :< Syntax.Function identifier _ _) -> LeafInfo (toCategoryName $ Categorizable info) (maybe "anonymous" toTermName identifier) + (info :< Syntax.Assignment identifier _) -> LeafInfo (toCategoryName $ Categorizable info) (toTermName identifier) + (info :< Syntax.MathAssignment identifier _) -> LeafInfo (toCategoryName $ Categorizable info) (toTermName identifier) -- Currently we cannot express the operator for an operator production from TreeSitter. Eventually we should be able to -- use the term name of the operator identifier when we have that production value. Until then, I'm using a placeholder value -- to indicate where that value should be when constructing DiffInfos. - (info :< Syntax.Operator _) -> [DiffInfo (toCategoryName $ Categorizable info) "x"] - (info :< Commented cs leaf) -> join (termToDiffInfo <$> cs) <> maybe [] (\leaf -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName leaf) ]) leaf - (info :< _) -> [ DiffInfo (toCategoryName $ Categorizable info) (toTermName term) ] + (info :< Syntax.Operator _) -> LeafInfo (toCategoryName $ Categorizable info) "x" + (info :< Commented cs leaf) -> BranchInfo (termToDiffInfo <$> cs <> maybeToList leaf) (toCategoryName $ Categorizable info) BCommented + (info :< _) -> LeafInfo (toCategoryName $ Categorizable info) (toTermName term) prependSummary :: Category -> DiffSummary DiffInfo -> DiffSummary DiffInfo prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary } diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 01b776db4..402c50a98 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -14,10 +14,7 @@ import Category import DiffSummary import Text.PrettyPrint.Leijen.Text (pretty) import Test.Hspec.QuickCheck -import Interpreter import Diff.Arbitrary -import Text.Megaparsec.Text -import Text.Megaparsec import Data.List (partition) arrayInfo :: Info @@ -30,16 +27,16 @@ testDiff :: Diff Text Info testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ]) testSummary :: DiffSummary DiffInfo -testSummary = DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [], patchAnnotations = [] } +testSummary = DiffSummary { patch = Insert (LeafInfo "string" "a"), parentAnnotations = [] } replacementSummary :: DiffSummary DiffInfo -replacementSummary = DiffSummary { patch = Replace (DiffInfo "string" "a") (DiffInfo "symbol" "b"), parentAnnotations = [ ArrayLiteral ], patchAnnotations = [] } +replacementSummary = DiffSummary { patch = Replace (LeafInfo "string" "a") (LeafInfo "symbol" "b"), parentAnnotations = [ ArrayLiteral ] } spec :: Spec spec = parallel $ do describe "diffSummary" $ do it "outputs a diff summary" $ do - diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (DiffInfo "string" "a"), parentAnnotations = [ ArrayLiteral ], patchAnnotations = [] } ] + diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (LeafInfo "string" "a"), parentAnnotations = [ ArrayLiteral ] } ] describe "show" $ do it "should print adds" $ show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) @@ -59,23 +56,15 @@ spec = parallel $ do (Indexed _) -> True (Fixed _) -> True _ -> False + isBranchInfo info = case info of + (BranchInfo _ _ _) -> True + (LeafInfo _ _) -> False isBranchNode :: DiffSummary DiffInfo -> Bool - isBranchNode summary = (not . null $ patchAnnotations summary) || (case patch summary of - (Insert diffInfo) -> termName diffInfo == "branch" - (Delete diffInfo) -> termName diffInfo == "branch" - (Replace i1 i2) -> termName i1 == "branch" || termName i2 == "branch") + isBranchNode summary = (case patch summary of + (Insert diffInfo) -> isBranchInfo diffInfo + (Delete diffInfo) -> isBranchInfo diffInfo + (Replace i1 i2) -> isBranchInfo i1 || isBranchInfo i2) in case (partition isBranchNode summaries, partition isIndexedOrFixed patches) of ((branchSummaries, otherSummaries), (branchPatches, otherPatches)) -> ((() <$) . patch <$> branchSummaries, (() <$) . patch <$> otherSummaries) `shouldBe` ((() <$) <$> branchPatches, (() <$) <$> otherPatches) - - -- ((() <$) <$> (patch <$> summaries)) `shouldBe` ((() <$) <$> patches) - -- [Insert (), Insert ()] == [ Insert () ] - -- explodePatch :: Patch (Syntax a) -> [Patch (Syntax a)] - -- explodePatch Indexed = explodePatch <$> children - - -- Patches of branch nodes with children should have a summary for each child that is not a branch node - -- Patches of branch nodes with children that are branch nodes shoudl have a summary for each of those children or one summary per branch if the branches are empty --- let xs = ArbitraryPure (Insert (ArbitraryTerm {annotation = Category.Operator .: RNil, syntax = Indexed [ArbitraryTerm {annotation = Program .: RNil, syntax = Leaf ""}]})) --- let xs = ArbitraryPure (Delete (ArbitraryTerm {annotation = Category.Case .: RNil, syntax = Fixed [ArbitraryTerm {annotation = Category.FunctionCall .: RNil, syntax = Leaf ""}]})) :: ArbitraryDiff Text (Record '[Category]) - -- ((() <$) . patch <$> summaries) `shouldBe` ((() <$) <$> otherPatches) From ea814432a699499fee0125820b9ae3ee48f89f8e Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 14:10:31 -0400 Subject: [PATCH 09/26] Add a property test to check number of LeafInfos in diff summaries match leaves in diff patches --- src/DiffSummary.hs | 2 +- test/DiffSummarySpec.hs | 82 ++++++++++++++++++++++++++++------------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 97e39ee47..81042ca37 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -133,7 +133,7 @@ instance P.Pretty (DiffSummary DiffInfo) where else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" toDoc = string . toS -diffSummary :: (HasCategory leaf, HasField fields Category, Hashable leaf, Show (Record fields), Show leaf, Ord (Record fields), Eq leaf, HasField fields Cost) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] +diffSummary :: (HasCategory leaf, HasField fields Category) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] diffSummary = cata $ \case -- Skip comments and leaves since they don't have any changes (Free (_ :< Leaf _)) -> [] diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 402c50a98..add67b280 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -42,29 +42,59 @@ spec = parallel $ do show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) it "prints a replacement" $ do show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) - prop "patches in summaries match the patches in diffs" $ - \a -> let - diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category, Cost]))) - summaries = diffSummary diff - patches = toList diff - isIndexedOrFixed :: Patch (Term a annotation) -> Bool - isIndexedOrFixed patch = case unwrap <$> patch of - (Insert syntax) -> isIndexedOrFixed' syntax - (Delete syntax) -> isIndexedOrFixed' syntax - (Replace s1 s2) -> isIndexedOrFixed' s1 || isIndexedOrFixed' s2 - isIndexedOrFixed' syntax = case syntax of - (Indexed _) -> True - (Fixed _) -> True - _ -> False - isBranchInfo info = case info of - (BranchInfo _ _ _) -> True - (LeafInfo _ _) -> False - isBranchNode :: DiffSummary DiffInfo -> Bool - isBranchNode summary = (case patch summary of - (Insert diffInfo) -> isBranchInfo diffInfo - (Delete diffInfo) -> isBranchInfo diffInfo - (Replace i1 i2) -> isBranchInfo i1 || isBranchInfo i2) - in - case (partition isBranchNode summaries, partition isIndexedOrFixed patches) of - ((branchSummaries, otherSummaries), (branchPatches, otherPatches)) -> - ((() <$) . patch <$> branchSummaries, (() <$) . patch <$> otherSummaries) `shouldBe` ((() <$) <$> branchPatches, (() <$) <$> otherPatches) + describe "DiffInfo" $ do + prop "patches in summaries match the patches in diffs" $ + \a -> let + diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category, Cost]))) + summaries = diffSummary diff + patches = toList diff + in + case (partition isBranchNode (patch <$> summaries), partition isIndexedOrFixed patches) of + ((branchPatches, otherPatches), (branchDiffPatches, otherDiffPatches)) -> + (() <$ branchPatches, () <$ otherPatches) `shouldBe` (() <$ branchDiffPatches, () <$ otherDiffPatches) + prop "generates one LeafInfo for each child in an arbitrary branch patch" $ + \a -> let + diff = (toDiff (a :: ArbitraryDiff Text (Record '[Category]))) + diffInfoPatches = patch <$> diffSummary diff + syntaxPatches = toList diff + extractLeaves :: DiffInfo -> [DiffInfo] + extractLeaves (BranchInfo children _ _) = join $ extractLeaves <$> children + extractLeaves leaf = [ leaf ] + + extractDiffLeaves :: Term Text (Record '[Category]) -> [ Term Text (Record '[Category]) ] + extractDiffLeaves term = case unwrap term of + (Indexed children) -> join $ extractDiffLeaves <$> children + (Fixed children) -> join $ extractDiffLeaves <$> children + Commented children leaf -> join $ extractDiffLeaves <$> children <> maybeToList leaf + _ -> [ term ] + in + case (partition isBranchNode diffInfoPatches, partition isIndexedOrFixed syntaxPatches) of + ((branchPatches, _), (diffPatches, _)) -> + let listOfLeaves = foldMap extractLeaves (join $ toList <$> branchPatches) + listOfDiffLeaves = foldMap extractDiffLeaves (join $ toList <$> diffPatches) + in + length listOfLeaves `shouldBe` length listOfDiffLeaves + + -- partitions arbitrary diff infos ([BranchInfo], [LeafInfo]) + -- partitions arbitrary patches ([Fixed/Indexed], [Other]) + -- Map [BranchInfo] -> [LeafInfo] + -- Map [Fixed/Indexed] -> [Children != Fixed/Indexed] + -- length [Children != Fixed/Indexed] == length [LeafInfo] + +isIndexedOrFixed :: Patch (Term a annotation) -> Bool +isIndexedOrFixed patch = case unwrap <$> patch of + (Insert syntax) -> isIndexedOrFixed' syntax + (Delete syntax) -> isIndexedOrFixed' syntax + (Replace s1 s2) -> isIndexedOrFixed' s1 || isIndexedOrFixed' s2 +isIndexedOrFixed' syntax = case syntax of + (Indexed _) -> True + (Fixed _) -> True + _ -> False +isBranchInfo info = case info of + (BranchInfo _ _ _) -> True + (LeafInfo _ _) -> False +isBranchNode :: Patch DiffInfo -> Bool +isBranchNode patch = case patch of + (Insert diffInfo) -> isBranchInfo diffInfo + (Delete diffInfo) -> isBranchInfo diffInfo + (Replace i1 i2) -> isBranchInfo i1 || isBranchInfo i2 \ No newline at end of file From 22eccb20e7b844dab6f1088285090492407b82f1 Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 14:11:16 -0400 Subject: [PATCH 10/26] unpin text-icu --- vendor/text-icu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/text-icu b/vendor/text-icu index 29f115a1d..6d07c2b20 160000 --- a/vendor/text-icu +++ b/vendor/text-icu @@ -1 +1 @@ -Subproject commit 29f115a1d0cb39b4c2f615d30579955eaa5bb855 +Subproject commit 6d07c2b2034f2bfdcd038de0d6a3ceca445f0221 From ed2f8bd57d03d1e9e178846fa7452409844b8abe Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 15:24:40 -0400 Subject: [PATCH 11/26] Delete comments --- test/DiffSummarySpec.hs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index add67b280..347f7aedd 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -75,12 +75,6 @@ spec = parallel $ do in length listOfLeaves `shouldBe` length listOfDiffLeaves - -- partitions arbitrary diff infos ([BranchInfo], [LeafInfo]) - -- partitions arbitrary patches ([Fixed/Indexed], [Other]) - -- Map [BranchInfo] -> [LeafInfo] - -- Map [Fixed/Indexed] -> [Children != Fixed/Indexed] - -- length [Children != Fixed/Indexed] == length [LeafInfo] - isIndexedOrFixed :: Patch (Term a annotation) -> Bool isIndexedOrFixed patch = case unwrap <$> patch of (Insert syntax) -> isIndexedOrFixed' syntax From ac36cf5bccba0302a61af41100c925a5078fb80b Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 17:06:41 -0400 Subject: [PATCH 12/26] Pretty print diff summaries as lists of LeafInfo Docs --- src/DiffSummary.hs | 36 ++++++++++++++++++++++++------------ src/Renderer/Summary.hs | 7 ++++--- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 81042ca37..8ff19c2d4 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,6 +1,6 @@ {-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-} -module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where +module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..), annotatedSummaries) where import Prologue hiding (snd, intercalate) import Diff @@ -15,7 +15,7 @@ import Data.Text as Text (intercalate) import Test.QuickCheck hiding (Fixed) import Patch.Arbitrary() import Data.Record -import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string) +import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string, Doc, punctuate, pretty) import qualified Text.PrettyPrint.Leijen.Text as P import Data.Hashable @@ -122,16 +122,28 @@ instance (Eq a, Arbitrary a) => Arbitrary (DiffSummary a) where arbitrary = DiffSummary <$> arbitrary <*> arbitrary shrink = genericShrink -instance P.Pretty (DiffSummary DiffInfo) where - pretty DiffSummary{..} = case patch of - Insert diffInfo -> "Added the" <+> squotes (toDoc $ termName diffInfo) <+> (toDoc $ categoryName diffInfo) P.<> maybeParentContext parentAnnotations - Delete diffInfo -> "Deleted the" <+> squotes (toDoc $ termName diffInfo) <+> (toDoc $ categoryName diffInfo) P.<> maybeParentContext parentAnnotations - Replace t1 t2 -> "Replaced the" <+> squotes (toDoc $ termName t1) <+> (toDoc $ categoryName t1) <+> "with the" <+> P.squotes (toDoc $ termName t2) <+> (toDoc $ categoryName t2) P.<> maybeParentContext parentAnnotations - where - maybeParentContext annotations = if null annotations - then "" - else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" - toDoc = string . toS +instance P.Pretty DiffInfo where + pretty LeafInfo{..} = squotes (string $ toSL termName) <+> (string $ toSL categoryName) + pretty BranchInfo{..} = mconcat $ punctuate (string "," <> space) (pretty <$> branches) + +annotatedSummaries :: DiffSummary DiffInfo -> [Text] +annotatedSummaries DiffSummary{..} = show . (P.<> maybeParentContext parentAnnotations) <$> summaries patch + +summaries :: Patch DiffInfo -> [P.Doc] +summaries (Insert info) = (("Added" <+> "the") <+>) <$> toLeafInfos info +summaries (Delete info) = (("Deleted" <+> "the") <+>) <$> toLeafInfos info +summaries (Replace i1 i2) = zipWith (\a b -> "Replaced" <+> "the" <+> a <+> "with the" <+> b) (toLeafInfos i1) (toLeafInfos i2) + +toLeafInfos :: DiffInfo -> [Doc] +toLeafInfos LeafInfo{..} = [ squotes (toDoc termName) <+> (toDoc categoryName) ] +toLeafInfos BranchInfo{..} = pretty <$> branches + +maybeParentContext :: [Category] -> Doc +maybeParentContext annotations = if null annotations + then "" + else space <> "in the" <+> (toDoc . intercalate "/" $ toCategoryName <$> annotations) <+> "context" +toDoc :: Text -> Doc +toDoc = string . toS diffSummary :: (HasCategory leaf, HasField fields Category) => Diff leaf (Record fields) -> [DiffSummary DiffInfo] diffSummary = cata $ \case diff --git a/src/Renderer/Summary.hs b/src/Renderer/Summary.hs index 4536e9d2c..c48bee36e 100644 --- a/src/Renderer/Summary.hs +++ b/src/Renderer/Summary.hs @@ -4,8 +4,9 @@ import Prologue import Renderer import DiffSummary import Data.Aeson -import Data.Text (pack) -import Text.PrettyPrint.Leijen.Text (pretty) summary :: Renderer -summary diff _ = toS . encode $ pack . show . pretty <$> diffSummary diff +summary diff _ = toS . encode $ annotatedTexts + where summaries = diffSummary diff + annotatedTexts :: [Text] + annotatedTexts = join $ annotatedSummaries <$> summaries From 78bb7779a4f14b92785944b15ae112ae4a37fcef Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 17:11:45 -0400 Subject: [PATCH 13/26] Test annotatedSummaries --- test/DiffSummarySpec.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 347f7aedd..045953e9b 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -39,9 +39,9 @@ spec = parallel $ do diffSummary testDiff `shouldBe` [ DiffSummary { patch = Insert (LeafInfo "string" "a"), parentAnnotations = [ ArrayLiteral ] } ] describe "show" $ do it "should print adds" $ - show (pretty testSummary) `shouldBe` ("Added the 'a' string" :: Text) + annotatedSummaries testSummary `shouldBe` ["Added the 'a' string"] it "prints a replacement" $ do - show (pretty replacementSummary) `shouldBe` ("Replaced the 'a' string with the 'b' symbol in the array context" :: Text) + annotatedSummaries replacementSummary `shouldBe` ["Replaced the 'a' string with the 'b' symbol in the array context"] describe "DiffInfo" $ do prop "patches in summaries match the patches in diffs" $ \a -> let From a4851ba06367537f82592ec91d7328f1970a9442 Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 15 Jul 2016 20:32:48 -0400 Subject: [PATCH 14/26] type signatures --- test/DiffSummarySpec.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 045953e9b..725292bf5 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -80,13 +80,18 @@ isIndexedOrFixed patch = case unwrap <$> patch of (Insert syntax) -> isIndexedOrFixed' syntax (Delete syntax) -> isIndexedOrFixed' syntax (Replace s1 s2) -> isIndexedOrFixed' s1 || isIndexedOrFixed' s2 + +isIndexedOrFixed' :: Syntax a f -> Bool isIndexedOrFixed' syntax = case syntax of (Indexed _) -> True (Fixed _) -> True _ -> False + +isBranchInfo :: DiffInfo -> Bool isBranchInfo info = case info of (BranchInfo _ _ _) -> True (LeafInfo _ _) -> False + isBranchNode :: Patch DiffInfo -> Bool isBranchNode patch = case patch of (Insert diffInfo) -> isBranchInfo diffInfo From ad4d36f7fd385036540b96e6f2a0e8c4ca0efc28 Mon Sep 17 00:00:00 2001 From: joshvera Date: Sun, 17 Jul 2016 11:18:31 -0400 Subject: [PATCH 15/26] Add a CoArbitrary instance to Category --- semantic-diff.cabal | 1 + src/Category.hs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index f762da7e6..84123584d 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -76,6 +76,7 @@ library , comonad , protolude , wl-pprint-text + , quickcheck-instances default-language: Haskell2010 default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards, LambdaCase ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j diff --git a/src/Category.hs b/src/Category.hs index 9e9bad4eb..8d500f2dc 100644 --- a/src/Category.hs +++ b/src/Category.hs @@ -3,6 +3,8 @@ module Category where import Prologue import Data.Hashable import Test.QuickCheck (oneof, Arbitrary, arbitrary, shrink) +import Test.QuickCheck.Arbitrary +import Data.Text (unpack) import Data.Text.Arbitrary() -- | A standardized category of AST node. Used to determine the semantics for @@ -72,14 +74,18 @@ data Category | Other Text deriving (Eq, Generic, Ord, Show) - -- Instances instance Hashable Category +instance CoArbitrary Text where + coarbitrary = coarbitrary . unpack +instance CoArbitrary Category where + coarbitrary = genericCoarbitrary + instance Arbitrary Category where arbitrary = oneof [ - pure Program + pure Program , pure Error , pure Boolean , pure BinaryOperator From bd985202ee176d9d23840610c14405d68a3e5708 Mon Sep 17 00:00:00 2001 From: joshvera Date: Sun, 17 Jul 2016 11:18:45 -0400 Subject: [PATCH 16/26] Add an error case to Objects --- src/Parser.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Parser.hs b/src/Parser.hs index f2402a253..1d72e4131 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -81,7 +81,9 @@ termConstructor source info = cofree . construct toTuple child | S.Indexed [key,value] <- unwrap child = [cofree (extract child :< S.Pair key value)] toTuple child | S.Fixed [key,value] <- unwrap child = [cofree (extract child :< S.Pair key value)] toTuple child | S.Leaf c <- unwrap child = [cofree (extract child :< S.Comment c)] + toTuple _ = [cofree (extract child :< S.Error S.Comment c)] construct children | isFixed (category info) = withDefaultInfo $ S.Fixed children + construct children | isError (category info) = withDefualtInfo $ S.Error children construct children = withDefaultInfo $ S.Indexed children From 5fe5da65b0b3d80dfc8a63e062fbfbb1fbcfb684 Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 22 Jul 2016 14:20:03 -0400 Subject: [PATCH 17/26] Use unwrap --- src/DiffSummary.hs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 6f15bc75b..332831270 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -144,20 +144,20 @@ diffSummary = cata $ \case (Pure (Replace t1 t2)) -> (\(info1, info2) -> DiffSummary (Replace info1 info2) []) <$> zip (termToDiffInfo t1) (termToDiffInfo t2) termToDiffInfo :: (HasCategory leaf, HasField fields Category) => Term leaf (Record fields) -> [DiffInfo] -termToDiffInfo term = case runCofree term of - (_ :< Leaf _) -> [ DiffInfo (toCategoryName term) (toTermName term) ] - (_ :< Indexed children) -> join $ termToDiffInfo <$> children - (_ :< Fixed children) -> join $ termToDiffInfo <$> children - (_ :< Syntax.FunctionCall identifier _) -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] - (_ :< Syntax.Ternary ternaryCondition _) -> [ DiffInfo (toCategoryName term) (toTermName ternaryCondition) ] - (_ :< Syntax.Function identifier _ _) -> [ DiffInfo (toCategoryName term) (maybe "anonymous" toTermName identifier) ] - (_ :< Syntax.Assignment identifier _) -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] - (_ :< Syntax.MathAssignment identifier _) -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] +termToDiffInfo term = case unwrap term of + Leaf _ -> [ DiffInfo (toCategoryName term) (toTermName term) ] + Indexed children -> join $ termToDiffInfo <$> children + Fixed children -> join $ termToDiffInfo <$> children + Syntax.FunctionCall identifier _ -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] + Syntax.Ternary ternaryCondition _ -> [ DiffInfo (toCategoryName term) (toTermName ternaryCondition) ] + Syntax.Function identifier _ _ -> [ DiffInfo (toCategoryName term) (maybe "anonymous" toTermName identifier) ] + Syntax.Assignment identifier _ -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] + Syntax.MathAssignment identifier _ -> [ DiffInfo (toCategoryName term) (toTermName identifier) ] -- Currently we cannot express the operator for an operator production from TreeSitter. Eventually we should be able to -- use the term name of the operator identifier when we have that production value. Until then, I'm using a placeholder value -- to indicate where that value should be when constructing DiffInfos. - (_ :< Syntax.Operator _) -> [DiffInfo (toCategoryName term) "x"] - (_ :< Commented cs leaf) -> join (termToDiffInfo <$> cs) <> maybe [] (\leaf -> [ DiffInfo (toCategoryName term) (toTermName leaf) ]) leaf + Syntax.Operator _ -> [DiffInfo (toCategoryName term) "x"] + Commented cs leaf -> join (termToDiffInfo <$> cs) <> maybe [] (\leaf -> [ DiffInfo (toCategoryName term) (toTermName leaf) ]) leaf _ -> [ DiffInfo (toCategoryName term) (toTermName term) ] prependSummary :: Category -> DiffSummary DiffInfo -> DiffSummary DiffInfo From b5119dab1e2556d0296e51335a4fc1245426c83b Mon Sep 17 00:00:00 2001 From: joshvera Date: Fri, 22 Jul 2016 14:51:08 -0400 Subject: [PATCH 18/26] Include Info --- test/DiffSummarySpec.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index cf989d683..297944eae 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -17,6 +17,7 @@ import Diff.Arbitrary import Data.List (partition) import Term.Arbitrary import Interpreter +import Info arrayInfo :: Record '[Category] arrayInfo = ArrayLiteral .: RNil From c7bdc8cd5b06b5848377c3353c8df58b343b2931 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Jul 2016 12:31:22 -0400 Subject: [PATCH 19/26] let it breathe --- src/DiffSummary.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index f43bc8f91..1e0e2a6a7 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-} + module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..), annotatedSummaries) where import Prologue hiding (snd, intercalate) From abacae0cc039a3a3690414c6799f5cf08230c448 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Jul 2016 12:35:22 -0400 Subject: [PATCH 20/26] redundant imports --- src/DiffSummary.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 1e0e2a6a7..1e3d9133b 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -7,7 +7,7 @@ import Prologue hiding (snd, intercalate) import Diff import Patch import Term -import Info (category, Cost) +import Info (category) import Syntax import Category import Data.Functor.Foldable as Foldable @@ -18,7 +18,6 @@ import Patch.Arbitrary() import Data.Record import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string, Doc, punctuate, pretty) import qualified Text.PrettyPrint.Leijen.Text as P -import Data.Hashable data DiffInfo = LeafInfo { categoryName :: Text, termName :: Text } | BranchInfo { branches :: [ DiffInfo ], categoryName :: Text, branchType :: Branch } From 1197d46d314bd6916bc13951eec381086ee7af0e Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Jul 2016 13:34:38 -0400 Subject: [PATCH 21/26] Remove megaparsec --- semantic-diff.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/semantic-diff.cabal b/semantic-diff.cabal index 6827745b4..7f0d8dce1 100644 --- a/semantic-diff.cabal +++ b/semantic-diff.cabal @@ -126,7 +126,6 @@ test-suite semantic-diff-test , free , recursion-schemes >= 4.1 , wl-pprint-text - , megaparsec if os(darwin) ghc-options: -threaded -rtsopts -with-rtsopts=-N -j else From 88b0cc327a326186dbfcf605951160b2bc1badc8 Mon Sep 17 00:00:00 2001 From: joshvera Date: Mon, 25 Jul 2016 13:35:52 -0400 Subject: [PATCH 22/26] Remove Categorizable newtype and HasFiel instance --- src/DiffSummary.hs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 1e3d9133b..2ed418747 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -67,10 +67,6 @@ class HasCategory a where instance HasCategory Text where toCategoryName = identity -newtype Categorizable a = Categorizable a -instance (HasField fields Category) => HasCategory (Categorizable (Record fields)) where - toCategoryName (Categorizable a)= toCategoryName $ category a - instance HasCategory Category where toCategoryName = \case ArrayLiteral -> "array" From 4809ad0f2f80704c5f45261e69f41e89afe4f3e1 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 26 Jul 2016 13:44:12 -0400 Subject: [PATCH 23/26] Use bind --- src/Renderer/Summary.hs | 4 +--- test/DiffSummarySpec.hs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Renderer/Summary.hs b/src/Renderer/Summary.hs index 17983625a..f4d2fe31b 100644 --- a/src/Renderer/Summary.hs +++ b/src/Renderer/Summary.hs @@ -10,7 +10,5 @@ import DiffSummary import Text.PrettyPrint.Leijen.Text (pretty) summary :: (HasField fields Category, HasField fields Range) => Renderer (Record fields) -summary diff _ = toS . encode $ annotatedTexts +summary diff _ = toS . encode $ summaries >>= annotatedSummaries where summaries = diffSummary diff - annotatedTexts :: [Text] - annotatedTexts = join $ annotatedSummaries <$> summaries diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 297944eae..bfe620510 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -78,7 +78,7 @@ spec = parallel $ do case (partition isBranchNode diffInfoPatches, partition isIndexedOrFixed syntaxPatches) of ((branchPatches, _), (diffPatches, _)) -> let listOfLeaves = foldMap extractLeaves (join $ toList <$> branchPatches) - listOfDiffLeaves = foldMap extractDiffLeaves (join $ toList <$> diffPatches) + listOfDiffLeaves = foldMap extractDiffLeaves (diffPatches >>= toList) in length listOfLeaves `shouldBe` length listOfDiffLeaves From 84edc4b8cefab86357639b9c72c9a706e3da6cba Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 26 Jul 2016 13:45:50 -0400 Subject: [PATCH 24/26] use bind --- test/DiffSummarySpec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index bfe620510..e798ab3eb 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -72,7 +72,7 @@ spec = parallel $ do extractDiffLeaves term = case unwrap term of (Indexed children) -> join $ extractDiffLeaves <$> children (Fixed children) -> join $ extractDiffLeaves <$> children - Commented children leaf -> join $ extractDiffLeaves <$> children <> maybeToList leaf + Commented children leaf -> children <> maybeToList leaf >>= extractDiffLeaves _ -> [ term ] in case (partition isBranchNode diffInfoPatches, partition isIndexedOrFixed syntaxPatches) of From de925c533d1df2ea74e416d614b8b9f6ffcacfa8 Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 26 Jul 2016 13:55:05 -0400 Subject: [PATCH 25/26] use Foldable.any --- test/DiffSummarySpec.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index e798ab3eb..88e675763 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -100,7 +100,4 @@ isBranchInfo info = case info of (LeafInfo _ _) -> False isBranchNode :: Patch DiffInfo -> Bool -isBranchNode patch = case patch of - (Insert diffInfo) -> isBranchInfo diffInfo - (Delete diffInfo) -> isBranchInfo diffInfo - (Replace i1 i2) -> isBranchInfo i1 || isBranchInfo i2 \ No newline at end of file +isBranchNode patch = any isBranchInfo From 044aa782c107421afee5fc86317a7a41a1f6153c Mon Sep 17 00:00:00 2001 From: joshvera Date: Tue, 26 Jul 2016 14:04:15 -0400 Subject: [PATCH 26/26] partially apply isBranchNode --- src/DiffSummary.hs | 1 - test/DiffSummarySpec.hs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DiffSummary.hs b/src/DiffSummary.hs index 2ed418747..0b66527d4 100644 --- a/src/DiffSummary.hs +++ b/src/DiffSummary.hs @@ -1,6 +1,5 @@ {-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-} - module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..), annotatedSummaries) where import Prologue hiding (snd, intercalate) diff --git a/test/DiffSummarySpec.hs b/test/DiffSummarySpec.hs index 88e675763..ab12b5546 100644 --- a/test/DiffSummarySpec.hs +++ b/test/DiffSummarySpec.hs @@ -100,4 +100,4 @@ isBranchInfo info = case info of (LeafInfo _ _) -> False isBranchNode :: Patch DiffInfo -> Bool -isBranchNode patch = any isBranchInfo +isBranchNode = any isBranchInfo