mirror of
https://github.com/github/semantic.git
synced 2024-12-01 00:33:59 +03:00
Merge branch 'master' into push-benchmarks-to-graphite
This commit is contained in:
commit
bfc28ec4a6
@ -63,7 +63,7 @@ library
|
||||
, comonad
|
||||
, protolude
|
||||
default-language: Haskell2010
|
||||
default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, OverloadedStrings, NoImplicitPrelude
|
||||
default-extensions: DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards
|
||||
ghc-options: -Wall -fno-warn-name-shadowing -O2 -threaded -fprof-auto "-with-rtsopts=-N -p -s -h -i0.1" -j
|
||||
|
||||
test-suite semantic-diff-test
|
||||
@ -99,7 +99,7 @@ test-suite semantic-diff-test
|
||||
else
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N -j -pgml=script/g++
|
||||
default-language: Haskell2010
|
||||
default-extensions: DeriveFunctor, DeriveGeneric, OverloadedStrings, NoImplicitPrelude
|
||||
default-extensions: DeriveFunctor, DeriveGeneric, FlexibleInstances, OverloadedStrings, NoImplicitPrelude, RecordWildCards
|
||||
if os(darwin)
|
||||
extra-libraries: stdc++ icuuc icudata icui18n
|
||||
if os(darwin)
|
||||
|
@ -104,10 +104,10 @@ alignBranch getRange children ranges = case intersectingChildren of
|
||||
line $ alignBranch getRange (remaining ++ symmetricalChildren ++ nonIntersectingChildren) (modifyJoin (advanceBy (drop 1)) ranges)
|
||||
lineAndRemaining _ Nothing = (identity, [])
|
||||
lineAndRemaining children (Just ranges) = let (intersections, remaining) = alignChildren getRange children ranges in
|
||||
((:) $ (,) <$> ranges `applyToBoth` intersections, remaining)
|
||||
((:) $ (,) <$> ranges `applyToBoth` (sortBy (compare `on` getRange) <$> intersections), remaining)
|
||||
|
||||
-- | Given a list of aligned children, produce lists of their intersecting first lines, and a list of the remaining lines/nonintersecting first lines.
|
||||
alignChildren :: (term -> Range) -> [Join These (term)] -> Join These Range -> (Both [term], [Join These term])
|
||||
alignChildren :: (term -> Range) -> [Join These term] -> Join These Range -> (Both [term], [Join These term])
|
||||
alignChildren _ [] _ = (both [] [], [])
|
||||
alignChildren getRange (first:rest) headRanges
|
||||
| ~(l, r) <- splitThese first
|
||||
|
@ -1,10 +1,7 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
module Category where
|
||||
|
||||
import Prologue
|
||||
import Data.String
|
||||
import Data.Set
|
||||
import Term
|
||||
|
||||
-- | A standardized category of AST node. Used to determine the semantics for
|
||||
-- | semantic diffing and define comparability of nodes.
|
||||
@ -28,17 +25,3 @@ data Category =
|
||||
-- | A non-standard category, which can be used for comparability.
|
||||
| Other String
|
||||
deriving (Eq, Show, Ord)
|
||||
|
||||
-- | The class of types that have categories.
|
||||
class Categorizable a where
|
||||
categories :: a -> Set Category
|
||||
|
||||
instance Categorizable annotation => Categorizable (Term a annotation) where
|
||||
categories term | (annotation :< _) <- runCofree term = categories annotation
|
||||
|
||||
-- | Test whether the categories from the categorizables intersect.
|
||||
comparable :: Categorizable a => a -> a -> Bool
|
||||
comparable a b = catsA == catsB || (not . Data.Set.null $ intersection catsA catsB)
|
||||
where
|
||||
catsA = categories a
|
||||
catsB = categories b
|
||||
|
@ -1,7 +1,6 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
module Data.Functor.Both where
|
||||
module Data.Functor.Both (Both,both, runBothWith, fst, snd, module X) where
|
||||
|
||||
import Data.Bifunctor.Join
|
||||
import Data.Bifunctor.Join as X
|
||||
import Prologue hiding (zipWith, fst, snd)
|
||||
import qualified Prologue
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-}
|
||||
{-# LANGUAGE TypeFamilies, TypeSynonymInstances #-}
|
||||
module Diff where
|
||||
|
||||
import Prologue
|
||||
|
@ -1,11 +1,11 @@
|
||||
{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables, FlexibleInstances, RecordWildCards #-}
|
||||
{-# LANGUAGE DataKinds, TypeFamilies, ScopedTypeVariables #-}
|
||||
module DiffSummary (DiffSummary(..), diffSummary, DiffInfo(..)) where
|
||||
|
||||
import Prologue hiding (fst, snd)
|
||||
import Data.String
|
||||
import Data.Maybe (fromJust)
|
||||
import Diff
|
||||
import Info
|
||||
import Info (Info, category)
|
||||
import Patch
|
||||
import Term
|
||||
import Syntax
|
||||
@ -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) -> toCategoryName . toCategory <$> head (extract <$> children)
|
||||
(_ :< Fixed children) -> toCategoryName . toCategory <$> head (extract <$> children)
|
||||
(_ :< Indexed children) -> toCategoryName . category <$> head (extract <$> children)
|
||||
(_ :< Fixed children) -> toCategoryName . category <$> head (extract <$> children)
|
||||
|
||||
class HasCategory a where
|
||||
toCategoryName :: a -> String
|
||||
@ -46,14 +46,14 @@ instance HasCategory Category where
|
||||
(Other s) -> s
|
||||
|
||||
instance HasCategory leaf => HasCategory (Term leaf Info) where
|
||||
toCategoryName = toCategoryName . toCategory . extract
|
||||
toCategoryName = toCategoryName . category . extract
|
||||
|
||||
data DiffSummary a = DiffSummary {
|
||||
patch :: Patch DiffInfo,
|
||||
parentAnnotations :: [DiffInfo]
|
||||
patch :: Patch a,
|
||||
parentAnnotations :: [a]
|
||||
} deriving (Eq, Functor)
|
||||
|
||||
instance Show a => Show (DiffSummary a) where
|
||||
instance Show (DiffSummary DiffInfo) where
|
||||
showsPrec _ DiffSummary{..} s = (++s) $ case patch of
|
||||
(Insert termInfo) -> "Added the " ++ "'" ++ fromJust (termName termInfo) ++ "' " ++ categoryName termInfo
|
||||
++ maybeParentContext parentAnnotations
|
||||
@ -70,15 +70,12 @@ diffSummary :: HasCategory leaf => Diff leaf Info -> [DiffSummary DiffInfo]
|
||||
diffSummary = cata diffSummary' where
|
||||
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
|
||||
diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (toCategoryName . toCategory $ snd infos) Nothing) <$> join (Prologue.toList children)
|
||||
diffSummary' (Free (infos :< Indexed children)) = prependSummary (DiffInfo (toCategoryName . category $ snd infos) Nothing) <$> join children
|
||||
diffSummary' (Free (infos :< Fixed children)) = prependSummary (DiffInfo (toCategoryName . category $ snd infos) Nothing) <$> join children
|
||||
diffSummary' (Free (infos :< Keyed children)) = prependSummary (DiffInfo (toCategoryName . category $ 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))) []]
|
||||
|
||||
prependSummary :: DiffInfo -> DiffSummary DiffInfo -> DiffSummary DiffInfo
|
||||
prependSummary annotation summary = summary { parentAnnotations = annotation : parentAnnotations summary }
|
||||
|
||||
toCategory :: Info -> Category
|
||||
toCategory info = fromMaybe (Other "Unknown") (maybeFirstCategory info)
|
||||
|
@ -1,18 +1,20 @@
|
||||
module Diffing where
|
||||
|
||||
import Prologue
|
||||
import Data.Bifunctor.Join
|
||||
import Prologue hiding (fst, snd)
|
||||
import qualified Data.ByteString.Char8 as B1
|
||||
import Data.Functor.Both
|
||||
import Data.Functor.Foldable
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.ICU.Detect as Detect
|
||||
import qualified Data.Text.ICU.Convert as Convert
|
||||
import Data.These
|
||||
import Diff
|
||||
import Info
|
||||
import Category
|
||||
import Interpreter
|
||||
import Language
|
||||
import Parser
|
||||
import Patch
|
||||
import Range
|
||||
import Renderer
|
||||
import Source hiding ((++))
|
||||
@ -36,8 +38,9 @@ lineByLineParser input = pure . cofree . root $ case foldl' annotateLeaves ([],
|
||||
(leaves, _) -> cofree <$> leaves
|
||||
where
|
||||
lines = actualLines input
|
||||
root children = Info (Range 0 $ length input) mempty (1 + fromIntegral (length children)) :< Indexed children
|
||||
leaf charIndex line = Info (Range charIndex $ charIndex + T.length line) mempty 1 :< Leaf line
|
||||
root children = let size = 1 + fromIntegral (length children) in
|
||||
Info (Range 0 $ length input) (Other "program") size size :< Indexed children
|
||||
leaf charIndex line = Info (Range charIndex $ charIndex + T.length line) (Other "program") 1 1 :< Leaf line
|
||||
annotateLeaves (accum, charIndex) line =
|
||||
(accum ++ [ leaf charIndex (toText line) ]
|
||||
, charIndex + length line)
|
||||
@ -52,14 +55,13 @@ breakDownLeavesByWord :: Source Char -> Term T.Text Info -> Term T.Text Info
|
||||
breakDownLeavesByWord source = cata replaceIn
|
||||
where
|
||||
replaceIn :: TermF T.Text Info (Term T.Text Info) -> Term T.Text Info
|
||||
replaceIn (Info range categories _ :< Leaf _)
|
||||
| ranges <- rangesAndWordsInSource range
|
||||
, length ranges > 1
|
||||
= cofree $ Info range categories (1 + fromIntegral (length ranges)) :< Indexed (makeLeaf categories <$> ranges)
|
||||
replaceIn (info :< syntax)
|
||||
= cofree $ info { size = 1 + sum (size . extract <$> syntax) } :< syntax
|
||||
replaceIn (info :< syntax) = let size' = 1 + sum (size . extract <$> syntax') in cofree $ info { size = size', cost = size' } :< syntax'
|
||||
where syntax' = case (ranges, syntax) of
|
||||
(_:_:_, Leaf _) -> Indexed (makeLeaf info <$> ranges)
|
||||
_ -> syntax
|
||||
ranges = rangesAndWordsInSource (characterRange info)
|
||||
rangesAndWordsInSource range = rangesAndWordsFrom (start range) (toString $ slice range source)
|
||||
makeLeaf categories (range, substring) = cofree $ Info range categories 1 :< Leaf (T.pack substring)
|
||||
makeLeaf info (range, substring) = cofree $ info { characterRange = range } :< Leaf (T.pack substring)
|
||||
|
||||
-- | Transcode a file to a unicode source.
|
||||
transcode :: B1.ByteString -> IO (Source Char)
|
||||
@ -82,15 +84,25 @@ diffFiles :: Parser -> Renderer -> Both SourceBlob -> IO T.Text
|
||||
diffFiles parser renderer sourceBlobs = do
|
||||
let sources = source <$> sourceBlobs
|
||||
terms <- sequence $ parser <$> sources
|
||||
|
||||
let replaceLeaves = breakDownLeavesByWord <$> sources
|
||||
pure $! renderer (runBothWith (diffTerms diffCostWithAbsoluteDifferenceOfCachedDiffSizes) $ replaceLeaves <*> terms) sourceBlobs
|
||||
let areNullOids = runJoin $ (== nullOid) . oid <$> sourceBlobs
|
||||
let textDiff = case areNullOids of
|
||||
(True, False) -> pure $ Insert (snd terms)
|
||||
(False, True) -> pure $ Delete (fst terms)
|
||||
(_, _) -> runBothWith (diffTerms construct ((==) `on` category . extract) 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))
|
||||
setCost info cost = info { cost = cost }
|
||||
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)))
|
||||
|
||||
-- | The sum of the node count of the diff’s patches.
|
||||
diffCostWithCachedTermSizes :: Diff a Info -> Integer
|
||||
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 (Join (before, after) :< _) -> abs $ size before - size after
|
||||
Pure patch -> sum $ size . extract <$> patch
|
||||
diffCostWithCachedTermSizes diff = case runFree diff of
|
||||
Free (info :< _) -> sum (cost <$> info)
|
||||
Pure patch -> sum (cost . extract <$> patch)
|
||||
|
@ -6,11 +6,5 @@ import Range
|
||||
|
||||
-- | An annotation for a source file, including the source range and semantic
|
||||
-- | categories.
|
||||
data Info = Info { characterRange :: !Range, categories :: !(Set Category), size :: !Integer }
|
||||
data Info = Info { characterRange :: !Range, category :: !Category, size :: !Integer, cost :: !Integer }
|
||||
deriving (Eq, Show)
|
||||
|
||||
instance Categorizable Info where
|
||||
categories = Info.categories
|
||||
|
||||
maybeFirstCategory :: (Categorizable a) => a -> Maybe Category
|
||||
maybeFirstCategory term = listToMaybe . toList $ Category.categories term
|
||||
|
@ -1,7 +1,6 @@
|
||||
module Interpreter (interpret, Comparable, diffTerms) where
|
||||
module Interpreter (Comparable, DiffConstructor, diffTerms) where
|
||||
|
||||
import Algorithm
|
||||
import Category
|
||||
import Data.Functor.Foldable
|
||||
import Data.Functor.Both
|
||||
import qualified Data.OrderedMap as Map
|
||||
@ -19,53 +18,52 @@ import Term
|
||||
-- | Returns whether two terms are comparable
|
||||
type Comparable a annotation = Term a annotation -> Term a annotation -> Bool
|
||||
|
||||
-- | Diff two terms, given the default Categorizable.comparable function and a function computing the cost of a given diff.
|
||||
diffTerms :: (Eq a, Eq annotation, Categorizable annotation) => Cost a annotation -> Term a annotation -> Term a annotation -> Diff a annotation
|
||||
diffTerms cost = interpret comparable cost
|
||||
-- | Constructs a diff from the CofreeF containing its annotation and syntax. This function has the opportunity to, for example, cache properties in the annotation.
|
||||
type DiffConstructor leaf annotation = CofreeF (Syntax leaf) (Both annotation) (Diff leaf annotation) -> Diff leaf annotation
|
||||
|
||||
-- | 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
|
||||
-- | Diff two terms, given a function that determines whether two terms can be compared and a cost function.
|
||||
diffTerms :: (Eq a, Eq annotation) => DiffConstructor a annotation -> Comparable a annotation -> Cost (Diff a annotation) -> Term a annotation -> Term a annotation -> Diff a annotation
|
||||
diffTerms construct comparable cost a b = fromMaybe (pure $ Replace a b) $ constructAndRun construct 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 (free . Free) runCofree <$> zipTerms a b
|
||||
constructAndRun :: (Eq a, Eq annotation) => DiffConstructor a annotation -> Comparable a annotation -> Cost (Diff a annotation) -> Term a annotation -> Term a annotation -> Maybe (Diff a annotation)
|
||||
constructAndRun _ comparable _ a b | not $ comparable a b = Nothing
|
||||
|
||||
constructAndRun comparable _ a b | not $ comparable a b = Nothing
|
||||
constructAndRun construct _ _ a b | (() <$ a) == (() <$ b) = hylo construct runCofree <$> zipTerms a b
|
||||
|
||||
constructAndRun comparable cost t1 t2 =
|
||||
run comparable cost $ algorithm a b where
|
||||
constructAndRun construct comparable cost t1 t2 =
|
||||
run construct comparable cost $ algorithm a b where
|
||||
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')) pure
|
||||
(annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2)
|
||||
annotate = pure . free . Free . (both annotation1 annotation2 :<)
|
||||
annotate = pure . construct . (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 comparable cost algorithm = case runFree algorithm of
|
||||
run :: (Eq a, Eq annotation) => DiffConstructor a annotation -> Comparable a annotation -> Cost (Diff a annotation) -> Algorithm a annotation (Diff a annotation) -> Maybe (Diff a annotation)
|
||||
run construct comparable cost algorithm = case runFree algorithm of
|
||||
Pure diff -> Just diff
|
||||
Free (Recursive t1 t2 f) -> run comparable cost . f $ recur a b where
|
||||
Free (Recursive t1 t2 f) -> run construct comparable cost . f $ recur a b where
|
||||
(annotation1 :< a, annotation2 :< b) = (runCofree t1, runCofree t2)
|
||||
annotate = free . Free . (both annotation1 annotation2 :<)
|
||||
annotate = construct . (both annotation1 annotation2 :<)
|
||||
|
||||
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 (Indexed a') (Indexed b') | length a' == length b' = annotate . Indexed $ zipWith (diffTerms construct comparable cost) a' b'
|
||||
recur (Fixed a') (Fixed b') | length a' == length b' = annotate . Fixed $ zipWith (diffTerms construct 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)
|
||||
interpretInBoth key x y = diffTerms construct comparable cost (x ! key) (y ! key)
|
||||
recur _ _ = pure $ Replace (cofree (annotation1 :< a)) (cofree (annotation2 :< b))
|
||||
|
||||
Free (ByKey a b f) -> run comparable cost $ f byKey where
|
||||
Free (ByKey a b f) -> run construct comparable cost $ f byKey where
|
||||
byKey = Map.fromList $ toKeyValue <$> List.union aKeys bKeys
|
||||
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))
|
||||
toKeyValue key = (key, diffTerms construct 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
|
||||
Free (ByIndex a b f) -> run construct comparable cost . f $ ses (constructAndRun construct comparable cost) cost a b
|
||||
|
@ -1,11 +1,9 @@
|
||||
module Parser where
|
||||
|
||||
import Prologue hiding (Constructor)
|
||||
import Data.String
|
||||
import Data.Text (pack)
|
||||
import Category
|
||||
import Info
|
||||
import Range
|
||||
import Syntax
|
||||
import Term
|
||||
import qualified Data.OrderedMap as Map
|
||||
@ -17,9 +15,8 @@ import Source
|
||||
-- | and aren't pure.
|
||||
type Parser = Source Char -> IO (Term Text Info)
|
||||
|
||||
-- | Given a source string, the term's range, production name, and
|
||||
-- | production/child pairs, construct the term.
|
||||
type Constructor = Source Char -> Range -> String -> [Term Text Info] -> Term Text Info
|
||||
-- | A function which constructs a term from a source string, annotation, and children.
|
||||
type Constructor = Source Char -> Info -> [Term Text Info] -> Term Text Info
|
||||
|
||||
-- | Categories that are treated as keyed nodes.
|
||||
keyedCategories :: Set.Set Category
|
||||
@ -30,24 +27,25 @@ fixedCategories :: Set.Set Category
|
||||
fixedCategories = Set.fromList [ BinaryOperator, Pair ]
|
||||
|
||||
-- | Should these categories be treated as keyed nodes?
|
||||
isKeyed :: Set.Set Category -> Bool
|
||||
isKeyed = not . Set.null . Set.intersection keyedCategories
|
||||
isKeyed :: Category -> Bool
|
||||
isKeyed = flip Set.member keyedCategories
|
||||
|
||||
-- | Should these categories be treated as fixed nodes?
|
||||
isFixed :: Set.Set Category -> Bool
|
||||
isFixed = not . Set.null . Set.intersection fixedCategories
|
||||
isFixed :: Category -> Bool
|
||||
isFixed = flip Set.member 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 . extract <$> children)) :< construct children)
|
||||
termConstructor :: Constructor
|
||||
termConstructor source info children = cofree (info :< syntax)
|
||||
where
|
||||
categories = mapping name
|
||||
syntax = construct children
|
||||
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 [] = Leaf . pack . toString $ slice (characterRange info) source
|
||||
construct children | isFixed (category info) = Fixed children
|
||||
construct children | isKeyed (category info) = Keyed . Map.fromList $ assignKey <$> children
|
||||
construct children = Indexed children
|
||||
assignKey node | Info _ categories _ :< Fixed (key : _) <- runCofree node, Set.member Pair categories = (getSubstring key, node)
|
||||
assignKey node = (getSubstring node, node)
|
||||
getSubstring term | Info range _ _ :< _ <- runCofree term = pack . toString $ slice range source
|
||||
assignKey node = case runCofree node of
|
||||
info :< Fixed (key : _) | Pair == category info -> (getSubstring key, node)
|
||||
_ -> (getSubstring node, node)
|
||||
getSubstring term = pack . toString $ slice (characterRange (extract term)) source
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
module Range where
|
||||
|
||||
import qualified Data.Char as Char
|
||||
@ -50,8 +49,7 @@ maybeLastIndex (Range _ end) = Just $ end - 1
|
||||
|
||||
-- | Test two ranges for intersection.
|
||||
intersectsRange :: Range -> Range -> Bool
|
||||
intersectsRange range1 range2 = isWellFormedAndNonEmpty $ intersectionRange range1 range2
|
||||
where isWellFormedAndNonEmpty range = start range < end range
|
||||
intersectsRange range1 range2 = start range1 < end range2 && start range2 < end range1
|
||||
|
||||
-- Return the (possibly empty, possibly ill-formed) intersection of two ranges.
|
||||
intersectionRange :: Range -> Range -> Range
|
||||
|
@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE FlexibleInstances, OverloadedStrings, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE OverloadedStrings, TypeSynonymInstances #-}
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
module Renderer.JSON (
|
||||
json
|
||||
@ -66,7 +66,7 @@ lineFields n term range = [ "number" .= n
|
||||
]
|
||||
|
||||
termFields :: (ToJSON recur, KeyValue kv) => Info -> Syntax leaf recur -> [kv]
|
||||
termFields (Info range categories _) syntax = "range" .= range : "categories" .= categories : case syntax of
|
||||
termFields Info{..} syntax = "range" .= characterRange : "category" .= category : case syntax of
|
||||
Leaf _ -> []
|
||||
Indexed c -> childrenFields c
|
||||
Fixed c -> childrenFields c
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
module Renderer.Split where
|
||||
|
||||
import Data.String
|
||||
@ -31,8 +30,8 @@ maybeFirst = foldr (const . Just) Nothing
|
||||
|
||||
-- | Add the first category from a Foldable of categories as a class name as a
|
||||
-- | class name on the markup, prefixed by `category-`.
|
||||
classifyMarkup :: Prologue.Foldable f => f Category -> Markup -> Markup
|
||||
classifyMarkup categories element = maybe element ((element !) . A.class_ . stringValue . styleName) $ maybeFirst categories
|
||||
classifyMarkup :: Category -> Markup -> Markup
|
||||
classifyMarkup category element = (element !) . A.class_ . stringValue $ styleName category
|
||||
|
||||
-- | Return the appropriate style name for the given category.
|
||||
styleName :: Category -> String
|
||||
@ -92,11 +91,11 @@ split diff blobs = TL.toStrict . renderHtml
|
||||
newtype Renderable a = Renderable a
|
||||
|
||||
instance ToMarkup f => ToMarkup (Renderable (Source Char, Info, Syntax a (f, Range))) where
|
||||
toMarkup (Renderable (source, Info range categories size, syntax)) = (! A.data_ (stringValue (show size))) . classifyMarkup categories $ case syntax of
|
||||
Leaf _ -> span . string . toString $ slice range source
|
||||
Indexed children -> ul . mconcat $ wrapIn li <$> contentElements source range children
|
||||
Fixed children -> ul . mconcat $ wrapIn li <$> contentElements source range children
|
||||
Keyed children -> dl . mconcat $ wrapIn dd <$> contentElements source range children
|
||||
toMarkup (Renderable (source, Info {..}, syntax)) = (! A.data_ (stringValue (show size))) . classifyMarkup category $ case syntax of
|
||||
Leaf _ -> span . string . toString $ slice characterRange source
|
||||
Indexed children -> ul . mconcat $ wrapIn li <$> contentElements source characterRange children
|
||||
Fixed children -> ul . mconcat $ wrapIn li <$> contentElements source characterRange children
|
||||
Keyed children -> dl . mconcat $ wrapIn dd <$> contentElements source characterRange children
|
||||
|
||||
contentElements :: (Foldable t, ToMarkup f) => Source Char -> Range -> t (f, Range) -> [Markup]
|
||||
contentElements source range children = let (elements, next) = foldr' (markupForContextAndChild source) ([], end range) children in
|
||||
@ -113,13 +112,13 @@ wrapIn _ l@Blaze.Comment{} = l
|
||||
wrapIn f p = f p
|
||||
|
||||
instance ToMarkup (Renderable (Source Char, Term a Info)) where
|
||||
toMarkup (Renderable (source, term)) = Prologue.fst $ cata (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) term
|
||||
toMarkup (Renderable (source, term)) = Prologue.fst $ cata (\ (info@(Info{..}) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), characterRange)) term
|
||||
|
||||
instance ToMarkup (Renderable (Source Char, SplitDiff a Info)) where
|
||||
toMarkup (Renderable (source, diff)) = Prologue.fst $ iter (\ (info@(Info range _ _) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), range)) $ toMarkupAndRange <$> diff
|
||||
toMarkup (Renderable (source, diff)) = Prologue.fst $ iter (\ (info@(Info{..}) :< syntax) -> (toMarkup $ Renderable (source, info, syntax), characterRange)) $ toMarkupAndRange <$> diff
|
||||
where toMarkupAndRange :: SplitPatch (Term a Info) -> (Markup, Range)
|
||||
toMarkupAndRange patch = let term@(Info range _ size :< _) = runCofree $ getSplitTerm patch in
|
||||
((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue (show size))) . toMarkup $ Renderable (source, cofree term), range)
|
||||
toMarkupAndRange patch = let term@(Info{..} :< _) = runCofree $ getSplitTerm patch in
|
||||
((div ! A.class_ (splitPatchToClassName patch) ! A.data_ (stringValue (show size))) . toMarkup $ Renderable (source, cofree term), characterRange)
|
||||
|
||||
instance ToMarkup a => ToMarkup (Renderable (Bool, Int, a)) where
|
||||
toMarkup (Renderable (hasChanges, num, line)) =
|
||||
|
@ -3,7 +3,9 @@ module Renderer.Summary where
|
||||
import Prologue
|
||||
import Renderer
|
||||
import DiffSummary
|
||||
import Data.Aeson
|
||||
import Data.ByteString.Builder
|
||||
import Data.Text (pack)
|
||||
|
||||
summary :: Renderer
|
||||
summary diff sources = pack . show $ diffSummary diff
|
||||
summary diff _ = toS . toLazyByteString . fromEncoding . foldable $ pack . show <$> diffSummary diff
|
||||
|
39
src/SES.hs
39
src/SES.hs
@ -2,29 +2,26 @@ module SES where
|
||||
|
||||
import Prologue
|
||||
import Patch
|
||||
import Diff
|
||||
import Term
|
||||
import qualified Data.Map as Map
|
||||
|
||||
-- | A function that maybe creates a diff from two terms.
|
||||
type Compare a annotation = Term a annotation -> Term a annotation -> Maybe (Diff a annotation)
|
||||
-- | Edit constructor for two terms, if comparable. Otherwise returns Nothing.
|
||||
type Compare term edit = term -> term -> Maybe edit
|
||||
|
||||
-- | A function that computes the cost of a diff.
|
||||
type Cost a annotation = Diff a annotation -> Integer
|
||||
-- | A function that computes the cost of an edit.
|
||||
type Cost edit = edit -> Integer
|
||||
|
||||
-- | Find the shortest edit script (diff) between two terms given a function to compute the cost.
|
||||
ses :: Compare a annotation -> Cost a annotation -> [Term a annotation] -> [Term a annotation] -> [Diff a annotation]
|
||||
ses :: Applicative edit => Compare term (edit (Patch term)) -> Cost (edit (Patch term)) -> [term] -> [term] -> [edit (Patch term)]
|
||||
ses diffTerms cost as bs = fst <$> evalState diffState Map.empty where
|
||||
diffState = diffAt diffTerms cost (0, 0) as bs
|
||||
|
||||
-- | Find the shortest edit script between two terms at a given vertex in the edit graph.
|
||||
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 _ _ _ [] [] = pure []
|
||||
diffAt _ cost _ [] bs = pure $ foldr toInsertions [] bs where
|
||||
toInsertions each = consWithCost cost (free . Pure . Insert $ each)
|
||||
diffAt _ cost _ as [] = pure $ foldr toDeletions [] as where
|
||||
toDeletions each = consWithCost cost (free . Pure . Delete $ each)
|
||||
diffAt diffTerms cost (i, j) (a : as) (b : bs) = do
|
||||
diffAt :: Applicative edit => Compare term (edit (Patch term)) -> Cost (edit (Patch term)) -> (Integer, Integer) -> [term] -> [term] -> State (Map.Map (Integer, Integer) [(edit (Patch term), Integer)]) [(edit (Patch term), Integer)]
|
||||
diffAt diffTerms cost (i, j) as bs
|
||||
| null as, null bs = pure []
|
||||
| null as = pure $ foldr insert [] bs
|
||||
| null bs = pure $ foldr delete [] as
|
||||
| (a : as) <- as, (b : bs) <- bs = do
|
||||
cachedDiffs <- get
|
||||
case Map.lookup (i, j) cachedDiffs of
|
||||
Just diffs -> pure diffs
|
||||
@ -34,19 +31,19 @@ diffAt diffTerms cost (i, j) (a : as) (b : bs) = do
|
||||
nomination <- fmap best $ case diffTerms a b of
|
||||
Just diff -> do
|
||||
diagonal <- recur (succ i, succ j) as bs
|
||||
pure [ delete down, insert right, consWithCost cost diff diagonal ]
|
||||
Nothing -> pure [ delete down, insert right ]
|
||||
pure [ delete a down, insert b right, consWithCost cost diff diagonal ]
|
||||
Nothing -> pure [ delete a down, insert b right ]
|
||||
cachedDiffs' <- get
|
||||
put $ Map.insert (i, j) nomination cachedDiffs'
|
||||
pure nomination
|
||||
where
|
||||
delete = consWithCost cost (free . Pure . Delete $ a)
|
||||
insert = consWithCost cost (free . Pure . Insert $ b)
|
||||
delete = consWithCost cost . pure . Delete
|
||||
insert = consWithCost cost . pure . Insert
|
||||
costOf [] = 0
|
||||
costOf ((_, c) : _) = c
|
||||
best = minimumBy (comparing costOf)
|
||||
recur = diffAt diffTerms cost
|
||||
|
||||
-- | Prepend a diff to the list with the cumulative cost.
|
||||
consWithCost :: Cost a annotation -> Diff a annotation -> [(Diff a annotation, Integer)] -> [(Diff a annotation, Integer)]
|
||||
consWithCost cost diff rest = (diff, cost diff + maybe 0 snd (fst <$> uncons rest)) : rest
|
||||
-- | Prepend an edit script and the cumulative cost onto the edit script.
|
||||
consWithCost :: Cost edit -> edit -> [(edit, Integer)] -> [(edit, Integer)]
|
||||
consWithCost cost edit rest = (edit, cost edit + maybe 0 snd (fst <$> uncons rest)) : rest
|
||||
|
@ -30,8 +30,8 @@ newtype Source a = Source { getVector :: Vector.Vector a }
|
||||
|
||||
-- | Map blobs with Nothing blobKind to empty blobs.
|
||||
idOrEmptySourceBlob :: SourceBlob -> SourceBlob
|
||||
idOrEmptySourceBlob blob = if isNothing (Source.blobKind blob)
|
||||
then (blob { Source.oid = nullOid, Source.blobKind = Nothing })
|
||||
idOrEmptySourceBlob blob = if isNothing (blobKind blob)
|
||||
then blob { oid = nullOid, blobKind = Nothing }
|
||||
else blob
|
||||
|
||||
nullOid :: String
|
||||
|
@ -1,4 +1,4 @@
|
||||
{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-}
|
||||
{-# LANGUAGE TypeFamilies, TypeSynonymInstances #-}
|
||||
module Term where
|
||||
|
||||
import Prologue
|
||||
|
@ -3,11 +3,11 @@ module TreeSitter where
|
||||
import Prologue hiding (Constructor)
|
||||
import Data.String
|
||||
import Category
|
||||
import Info
|
||||
import Language
|
||||
import Parser
|
||||
import Range
|
||||
import Source
|
||||
import qualified Data.Set as Set
|
||||
import Foreign
|
||||
import Foreign.C.String
|
||||
import Text.Parser.TreeSitter hiding (Language(..))
|
||||
@ -21,33 +21,33 @@ treeSitterParser language grammar contents = do
|
||||
withCString (toString contents) (\source -> do
|
||||
ts_document_set_input_string document source
|
||||
ts_document_parse document
|
||||
term <- documentToTerm (termConstructor $ categoriesForLanguage language) document contents
|
||||
term <- documentToTerm language document contents
|
||||
ts_document_free document
|
||||
pure term)
|
||||
|
||||
-- Given a language and a node name, return the correct categories.
|
||||
categoriesForLanguage :: Language -> String -> Set.Set Category
|
||||
categoriesForLanguage :: Language -> String -> Category
|
||||
categoriesForLanguage language name = case (language, name) of
|
||||
(JavaScript, "object") -> Set.singleton DictionaryLiteral
|
||||
(JavaScript, "rel_op") -> Set.singleton BinaryOperator -- relational operator, e.g. >, <, <=, >=, ==, !=
|
||||
(JavaScript, "object") -> DictionaryLiteral
|
||||
(JavaScript, "rel_op") -> BinaryOperator -- relational operator, e.g. >, <, <=, >=, ==, !=
|
||||
|
||||
(Ruby, "hash") -> Set.singleton DictionaryLiteral
|
||||
(Ruby, "hash") -> DictionaryLiteral
|
||||
_ -> defaultCategoryForNodeName name
|
||||
|
||||
-- | Given a node name from TreeSitter, return the correct categories.
|
||||
defaultCategoryForNodeName :: String -> Set.Set Category
|
||||
defaultCategoryForNodeName :: String -> Category
|
||||
defaultCategoryForNodeName name = case name of
|
||||
"function_call" -> Set.singleton FunctionCall
|
||||
"pair" -> Set.singleton Pair
|
||||
"string" -> Set.singleton StringLiteral
|
||||
"integer" -> Set.singleton IntegerLiteral
|
||||
"symbol" -> Set.singleton SymbolLiteral
|
||||
"array" -> Set.singleton ArrayLiteral
|
||||
_ -> Set.singleton (Other name)
|
||||
"function_call" -> FunctionCall
|
||||
"pair" -> Pair
|
||||
"string" -> StringLiteral
|
||||
"integer" -> IntegerLiteral
|
||||
"symbol" -> SymbolLiteral
|
||||
"array" -> ArrayLiteral
|
||||
_ -> (Other name)
|
||||
|
||||
-- | Given a constructor and a tree sitter document, return a parser.
|
||||
documentToTerm :: Constructor -> Ptr Document -> Parser
|
||||
documentToTerm constructor document contents = alloca $ \ root -> do
|
||||
-- | Return a parser for a tree sitter language & document.
|
||||
documentToTerm :: Language -> Ptr Document -> Parser
|
||||
documentToTerm language document contents = alloca $ \ root -> do
|
||||
ts_document_root_node_p document root
|
||||
toTerm root
|
||||
where toTerm node = do
|
||||
@ -58,7 +58,9 @@ documentToTerm constructor document contents = alloca $ \ root -> do
|
||||
-- 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 <- pure $! Range { start = fromIntegral $ ts_node_p_start_char node, end = fromIntegral $ ts_node_p_end_char node }
|
||||
|
||||
pure $! constructor contents range name children
|
||||
let size' = 1 + sum (size . extract <$> children)
|
||||
let info = Info range (categoriesForLanguage language name) size' size'
|
||||
pure $! termConstructor contents info children
|
||||
getChild node n out = do
|
||||
_ <- ts_node_p_named_child node n out
|
||||
toTerm out
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
module AlignmentSpec where
|
||||
|
||||
import Alignment
|
||||
@ -22,6 +21,7 @@ import Range
|
||||
import qualified Source
|
||||
import SplitDiff
|
||||
import Syntax
|
||||
import Category
|
||||
import Term
|
||||
import Test.Hspec
|
||||
import Test.Hspec.QuickCheck
|
||||
@ -257,7 +257,7 @@ align :: Both (Source.Source Char) -> ConstructibleFree (Patch (Term String Info
|
||||
align sources = PrettyDiff sources . fmap (fmap (getRange &&& identity)) . alignDiff sources . deconstruct
|
||||
|
||||
info :: Int -> Int -> Info
|
||||
info = ((\ r -> Info r mempty 0) .) . Range
|
||||
info start end = Info (Range start end) StringLiteral 0 0
|
||||
|
||||
prettyDiff :: Both (Source.Source Char) -> [Join These (ConstructibleFree (SplitPatch (Term String Info)) Info)] -> PrettyDiff (SplitDiff String Info)
|
||||
prettyDiff sources = PrettyDiff sources . fmap (fmap ((getRange &&& identity) . deconstruct))
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
module ArbitraryTerm where
|
||||
|
||||
@ -8,7 +7,6 @@ import Data.Functor.Both
|
||||
import Data.Functor.Foldable
|
||||
import qualified Data.OrderedMap as Map
|
||||
import qualified Data.List as List
|
||||
import qualified Data.Set as Set
|
||||
import Data.Text.Arbitrary ()
|
||||
import Data.These
|
||||
import Info
|
||||
@ -46,12 +44,6 @@ instance (Eq a, Eq annotation, Arbitrary a, Arbitrary annotation) => Arbitrary (
|
||||
|
||||
data CategorySet = A | B | C | D deriving (Eq, Show)
|
||||
|
||||
instance Categorizable CategorySet where
|
||||
categories A = Set.fromList [ Other "a" ]
|
||||
categories B = Set.fromList [ Other "b" ]
|
||||
categories C = Set.fromList [ Other "c" ]
|
||||
categories D = Set.fromList [ Other "d" ]
|
||||
|
||||
instance Arbitrary CategorySet where
|
||||
arbitrary = elements [ A, B, C, D ]
|
||||
|
||||
@ -80,4 +72,4 @@ instance Arbitrary a => Arbitrary (Source a) where
|
||||
|
||||
arbitraryLeaf :: Gen (Source Char, Info, Syntax (Source Char) f)
|
||||
arbitraryLeaf = toTuple <$> arbitrary
|
||||
where toTuple string = (string, Info (Range 0 $ length string) mempty 1, Leaf string)
|
||||
where toTuple string = (string, Info (Range 0 $ length string) StringLiteral 1 0, Leaf string)
|
||||
|
@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
module CorpusSpec where
|
||||
|
||||
import System.IO
|
||||
@ -32,7 +33,7 @@ spec = parallel $ do
|
||||
examples "test/diffs/" `shouldNotReturn` []
|
||||
|
||||
where
|
||||
runTestsIn :: String -> (T.Text -> T.Text -> Expectation) -> SpecWith ()
|
||||
runTestsIn :: FilePath -> (Verbatim -> Verbatim -> Expectation) -> SpecWith ()
|
||||
runTestsIn directory matcher = do
|
||||
paths <- runIO $ examples directory
|
||||
let tests = correctTests =<< paths
|
||||
@ -57,7 +58,7 @@ examples directory = do
|
||||
let keys = Set.unions $ keysSet <$> [as, bs]
|
||||
pure $ (\name -> (both (as ! name) (bs ! name), Map.lookup name jsons, Map.lookup name patches, Map.lookup name splits)) <$> sort (Set.toList keys)
|
||||
where
|
||||
globFor :: String -> IO [FilePath]
|
||||
globFor :: FilePath -> IO [FilePath]
|
||||
globFor p = globDir1 (compile p) directory
|
||||
toDict list = Map.fromList ((normalizeName <$> list) `zip` list)
|
||||
|
||||
@ -68,14 +69,21 @@ 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 -> Both FilePath -> Maybe FilePath -> (T.Text -> T.Text -> Expectation) -> Expectation
|
||||
testDiff :: Renderer -> Both FilePath -> Maybe FilePath -> (Verbatim -> Verbatim -> Expectation) -> Expectation
|
||||
testDiff renderer paths diff matcher = do
|
||||
sources <- sequence $ readAndTranscodeFile <$> paths
|
||||
actual <- diffFiles parser renderer (sourceBlobs sources)
|
||||
actual <- Verbatim <$> diffFiles parser renderer (sourceBlobs sources)
|
||||
case diff of
|
||||
Nothing -> matcher actual actual
|
||||
Just file -> do
|
||||
expected <- T.pack <$> readFile file
|
||||
expected <- Verbatim . T.pack <$> readFile file
|
||||
matcher actual expected
|
||||
where parser = parserForFilepath (fst paths)
|
||||
sourceBlobs sources = pure S.SourceBlob <*> sources <*> pure mempty <*> paths <*> pure (Just S.defaultPlainBlob)
|
||||
|
||||
-- | A wrapper around `Text` with a more readable `Show` instance.
|
||||
newtype Verbatim = Verbatim Text
|
||||
deriving (Eq, NFData)
|
||||
|
||||
instance Show Verbatim where
|
||||
showsPrec _ (Verbatim text) = ('\n':) . (T.unpack text ++)
|
||||
|
@ -9,22 +9,21 @@ import Syntax
|
||||
import Patch
|
||||
import Range
|
||||
import Category
|
||||
import Data.Set
|
||||
import DiffSummary
|
||||
|
||||
arrayInfo :: Info
|
||||
arrayInfo = Info (rangeAt 0) (singleton ArrayLiteral) 2
|
||||
arrayInfo = Info (rangeAt 0) ArrayLiteral 2 0
|
||||
|
||||
literalInfo :: Info
|
||||
literalInfo = Info (rangeAt 1) (singleton StringLiteral) 1
|
||||
literalInfo = Info (rangeAt 1) StringLiteral 1 0
|
||||
|
||||
testDiff :: Diff String Info
|
||||
testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "a")) ])
|
||||
|
||||
testSummary :: DiffSummary Char
|
||||
testSummary :: DiffSummary DiffInfo
|
||||
testSummary = DiffSummary { patch = Insert (DiffInfo "string" (Just "a")), parentAnnotations = [] }
|
||||
|
||||
replacementSummary :: DiffSummary Char
|
||||
replacementSummary :: DiffSummary DiffInfo
|
||||
replacementSummary = DiffSummary { patch = Replace (DiffInfo "string" (Just "a")) (DiffInfo "symbol" (Just "b")), parentAnnotations = [ (DiffInfo "array" (Just "switch {}")) ] }
|
||||
|
||||
spec :: Spec
|
||||
|
@ -14,8 +14,8 @@ spec :: Spec
|
||||
spec = parallel $
|
||||
describe "interpret" $
|
||||
it "returns a replacement when comparing two unicode equivalent terms" $
|
||||
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"))))
|
||||
I.diffTerms (free . Free) ((==) `on` extract) diffCost (cofree (Info range StringLiteral 0 0 :< Leaf "t\776")) (cofree (Info range2 StringLiteral 0 0 :< Leaf "\7831")) `shouldBe`
|
||||
free (Pure (Replace (cofree (Info range StringLiteral 0 0 :< Leaf "t\776")) (cofree (Info range2 StringLiteral 0 0 :< Leaf "\7831"))))
|
||||
|
||||
where
|
||||
range = Range 0 2
|
||||
|
@ -2,17 +2,16 @@ module PatchOutputSpec where
|
||||
|
||||
import Prologue
|
||||
import Data.Functor.Both
|
||||
import Data.String
|
||||
import Diff
|
||||
import Info
|
||||
import Range
|
||||
import Renderer.Patch
|
||||
import Source
|
||||
import Syntax
|
||||
import Category
|
||||
import Test.Hspec
|
||||
|
||||
spec :: Spec
|
||||
spec = parallel $
|
||||
describe "hunks" $
|
||||
it "empty diffs have empty hunks" $
|
||||
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 = pure 0, changes = [], trailingContext = []}]
|
||||
hunks (free . Free $ pure (Info (Range 0 0) StringLiteral 1 0) :< Leaf "") (both (SourceBlob (fromList "") "abcde" "path2.txt" (Just defaultPlainBlob)) (SourceBlob (fromList "") "xyz" "path2.txt" (Just defaultPlainBlob))) `shouldBe` [Hunk {offset = pure 0, changes = [], trailingContext = []}]
|
||||
|
@ -6,7 +6,6 @@ import Data.Text.Arbitrary ()
|
||||
|
||||
import Prologue
|
||||
import Data.String
|
||||
import Category
|
||||
import Interpreter
|
||||
import Diff
|
||||
import ArbitraryTerm
|
||||
@ -19,9 +18,9 @@ spec = parallel $ do
|
||||
|
||||
describe "Diff" $ do
|
||||
prop "equality is reflexive" $
|
||||
\ a b -> let diff = interpret comparable diffCost (unTerm a) (unTerm (b :: ArbitraryTerm String CategorySet)) in
|
||||
\ a b -> let diff = diffTerms (free . Free) ((==) `on` extract) diffCost (unTerm a) (unTerm (b :: ArbitraryTerm String CategorySet)) in
|
||||
diff == diff
|
||||
|
||||
prop "equal terms produce identity diffs" $
|
||||
\ a -> let term = unTerm (a :: ArbitraryTerm String CategorySet) in
|
||||
diffCost (interpret comparable diffCost term term) == 0
|
||||
diffCost (diffTerms (free . Free) ((==) `on` extract) diffCost term term) == 0
|
||||
|
2
test/crashers/573.A.js
Normal file
2
test/crashers/573.A.js
Normal file
@ -0,0 +1,2 @@
|
||||
if (a &&
|
||||
b) {}
|
1
test/crashers/573.B.js
Normal file
1
test/crashers/573.B.js
Normal file
@ -0,0 +1 @@
|
||||
if (a && b) {}
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html><head><link rel="stylesheet" href="style.css"></head><body><table class="diff"><colgroup><col width="40"><col><col width="40"><col></colgroup><tr><td class="blob-num">1</td><td class="blob-code"><ul class="category-program"><li><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></td>
|
||||
<td class="blob-num">1</td><td class="blob-code"><ul class="category-program"><li><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num blob-num-empty empty-cell"></td><td class="blob-code blob-code-empty empty-cell"></td>
|
||||
<td class="blob-num blob-num-replacement">2</td><td class="blob-code blob-code-replacement"><ul class="category-program"><li><div class="patch insert" data="5"><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">world</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></div></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">2</td><td class="blob-code"><ul class="category-program"></ul></td>
|
||||
<td class="blob-num">3</td><td class="blob-code"><ul class="category-program"></ul></td>
|
||||
|
||||
</tr></table></body></html>
|
@ -1,24 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html><head><link rel="stylesheet" href="style.css"></head><body><table class="diff"><colgroup><col width="40"><col><col width="40"><col></colgroup><tr><td class="blob-num">1</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement">if (<li><span class="category-true">true</span></li>) <li><ul class="category-statement_block">{
|
||||
</ul></li></ul></li></ul></td>
|
||||
<td class="blob-num">1</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement">if (<li><span class="category-true">true</span></li>) <li><ul class="category-statement_block">{
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">2</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement"><li><ul class="category-statement_block"> <li><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
<td class="blob-num">2</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement"><li><ul class="category-statement_block"> <li><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num blob-num-empty empty-cell"></td><td class="blob-code blob-code-empty empty-cell"></td>
|
||||
<td class="blob-num blob-num-replacement">3</td><td class="blob-code blob-code-replacement"><ul class="category-program"><li><ul class="category-if_statement"><li><ul class="category-statement_block"> <li><div class="patch insert" data="5"><ul class="category-expression_statement"><li><ul class="category-function_call"><li><ul class="category-member_access"><li><span class="category-identifier">console</span></li>.<li><span class="category-identifier">log</span></li></ul></li>(<li><ul class="category-arguments"><li><ul class="category-string"><li><span class="category-string">'</span></li><li><span class="category-string">world</span></li><li><span class="category-string">'</span></li></ul></li></ul></li>)</ul></li>;</ul></div></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">3</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement"><li><ul class="category-statement_block">}</ul></li><li><ul class="category-statement_block"></ul></li></ul></li>
|
||||
</ul></td>
|
||||
<td class="blob-num">4</td><td class="blob-code"><ul class="category-program"><li><ul class="category-if_statement"><li><ul class="category-statement_block">}</ul></li></ul></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">4</td><td class="blob-code"><ul class="category-program"></ul></td>
|
||||
<td class="blob-num">5</td><td class="blob-code"><ul class="category-program"></ul></td>
|
||||
|
||||
</tr></table></body></html>
|
@ -1 +1 @@
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,2],"categories":["program"],"children":[{"range":[0,2],"categories":["expression_statement"],"children":[{"range":[0,2],"categories":["DictionaryLiteral"],"children":{}}]}]}],"range":[0,2],"hasChanges":false},{"number":1,"terms":[{"range":[0,2],"categories":["program"],"children":[{"range":[0,2],"categories":["expression_statement"],"children":[{"range":[0,2],"categories":["DictionaryLiteral"],"children":{}}]}]}],"range":[0,2],"hasChanges":false}],[{"number":2,"terms":[{"range":[2,12],"categories":["program"],"children":[{"range":[2,12],"categories":["expression_statement"],"children":[{"range":[2,12],"categories":["DictionaryLiteral"],"children":{"\"b\"":{"range":[4,10],"categories":["Pair"],"children":[{"range":[4,7],"categories":["StringLiteral"],"children":[{"range":[4,5],"categories":["StringLiteral"]},{"range":[5,6],"categories":["StringLiteral"]},{"range":[6,7],"categories":["StringLiteral"]}]},{"patch":"replace","range":[9,10],"categories":["number"]}]}}}]}]}],"range":[2,12],"hasChanges":true},{"number":2,"terms":[{"range":[2,12],"categories":["program"],"children":[{"range":[2,12],"categories":["expression_statement"],"children":[{"range":[2,12],"categories":["DictionaryLiteral"],"children":{"\"b\"":{"range":[4,10],"categories":["Pair"],"children":[{"range":[4,7],"categories":["StringLiteral"],"children":[{"range":[4,5],"categories":["StringLiteral"]},{"range":[5,6],"categories":["StringLiteral"]},{"range":[6,7],"categories":["StringLiteral"]}]},{"patch":"replace","range":[9,10],"categories":["number"]}]}}}]}]}],"range":[2,12],"hasChanges":true}],[{"number":3,"terms":[{"range":[12,21],"categories":["program"],"children":[{"range":[12,21],"categories":["expression_statement"],"children":[{"range":[12,21],"categories":["DictionaryLiteral"],"children":{"\"a\"":{"range":[14,20],"categories":["Pair"],"children":[{"range":[14,17],"categories":["StringLiteral"],"children":[{"range":[14,15],"categories":["StringLiteral"]},{"range":[15,16],"categories":["StringLiteral"]},{"range":[16,17],"categories":["StringLiteral"]}]},{"range":[19,20],"categories":["number"]}]}}}]}]}],"range":[12,21],"hasChanges":false},{"number":3,"terms":[{"range":[12,21],"categories":["program"],"children":[{"range":[12,21],"categories":["expression_statement"],"children":[{"range":[12,21],"categories":["DictionaryLiteral"],"children":{"\"a\"":{"range":[14,20],"categories":["Pair"],"children":[{"range":[14,17],"categories":["StringLiteral"],"children":[{"range":[14,15],"categories":["StringLiteral"]},{"range":[15,16],"categories":["StringLiteral"]},{"range":[16,17],"categories":["StringLiteral"]}]},{"range":[19,20],"categories":["number"]}]}}}]}]}],"range":[12,21],"hasChanges":false}],[{"number":4,"terms":[{"range":[21,23],"categories":["program"],"children":[{"range":[21,23],"categories":["expression_statement"],"children":[{"range":[21,22],"categories":["DictionaryLiteral"],"children":{}}]}]}],"range":[21,23],"hasChanges":false},{"number":4,"terms":[{"range":[21,23],"categories":["program"],"children":[{"range":[21,23],"categories":["expression_statement"],"children":[{"range":[21,22],"categories":["DictionaryLiteral"],"children":{}}]}]}],"range":[21,23],"hasChanges":false}],[{"number":5,"terms":[{"range":[23,23],"categories":["program"],"children":[]}],"range":[23,23],"hasChanges":false},{"number":5,"terms":[{"range":[23,23],"categories":["program"],"children":[]}],"range":[23,23],"hasChanges":false}]],"oids":["",""],"paths":["test/diffs/dictionary.A.js","test/diffs/dictionary.B.js"]}
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,2],"category":"program","children":[{"range":[0,2],"category":"expression_statement","children":[{"range":[0,2],"category":"DictionaryLiteral","children":{}}]}]}],"range":[0,2],"hasChanges":false},{"number":1,"terms":[{"range":[0,2],"category":"program","children":[{"range":[0,2],"category":"expression_statement","children":[{"range":[0,2],"category":"DictionaryLiteral","children":{}}]}]}],"range":[0,2],"hasChanges":false}],[{"number":2,"terms":[{"range":[2,12],"category":"program","children":[{"range":[2,12],"category":"expression_statement","children":[{"range":[2,12],"category":"DictionaryLiteral","children":{"\"b\"":{"range":[4,10],"category":"Pair","children":[{"range":[4,7],"category":"StringLiteral","children":[{"range":[4,5],"category":"StringLiteral"},{"range":[5,6],"category":"StringLiteral"},{"range":[6,7],"category":"StringLiteral"}]},{"patch":"replace","range":[9,10],"category":"number"}]}}}]}]}],"range":[2,12],"hasChanges":true},{"number":2,"terms":[{"range":[2,12],"category":"program","children":[{"range":[2,12],"category":"expression_statement","children":[{"range":[2,12],"category":"DictionaryLiteral","children":{"\"b\"":{"range":[4,10],"category":"Pair","children":[{"range":[4,7],"category":"StringLiteral","children":[{"range":[4,5],"category":"StringLiteral"},{"range":[5,6],"category":"StringLiteral"},{"range":[6,7],"category":"StringLiteral"}]},{"patch":"replace","range":[9,10],"category":"number"}]}}}]}]}],"range":[2,12],"hasChanges":true}],[{"number":3,"terms":[{"range":[12,21],"category":"program","children":[{"range":[12,21],"category":"expression_statement","children":[{"range":[12,21],"category":"DictionaryLiteral","children":{"\"a\"":{"range":[14,20],"category":"Pair","children":[{"range":[14,17],"category":"StringLiteral","children":[{"range":[14,15],"category":"StringLiteral"},{"range":[15,16],"category":"StringLiteral"},{"range":[16,17],"category":"StringLiteral"}]},{"range":[19,20],"category":"number"}]}}}]}]}],"range":[12,21],"hasChanges":false},{"number":3,"terms":[{"range":[12,21],"category":"program","children":[{"range":[12,21],"category":"expression_statement","children":[{"range":[12,21],"category":"DictionaryLiteral","children":{"\"a\"":{"range":[14,20],"category":"Pair","children":[{"range":[14,17],"category":"StringLiteral","children":[{"range":[14,15],"category":"StringLiteral"},{"range":[15,16],"category":"StringLiteral"},{"range":[16,17],"category":"StringLiteral"}]},{"range":[19,20],"category":"number"}]}}}]}]}],"range":[12,21],"hasChanges":false}],[{"number":4,"terms":[{"range":[21,23],"category":"program","children":[{"range":[21,23],"category":"expression_statement","children":[{"range":[21,22],"category":"DictionaryLiteral","children":{}}]}]}],"range":[21,23],"hasChanges":false},{"number":4,"terms":[{"range":[21,23],"category":"program","children":[{"range":[21,23],"category":"expression_statement","children":[{"range":[21,22],"category":"DictionaryLiteral","children":{}}]}]}],"range":[21,23],"hasChanges":false}],[{"number":5,"terms":[{"range":[23,23],"category":"program","children":[]}],"range":[23,23],"hasChanges":false},{"number":5,"terms":[{"range":[23,23],"category":"program","children":[]}],"range":[23,23],"hasChanges":false}]],"oids":["",""],"paths":["test/diffs/dictionary.A.js","test/diffs/dictionary.B.js"]}
|
14
test/diffs/insert.split.js
Normal file
14
test/diffs/insert.split.js
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html><head><link rel="stylesheet" href="style.css"></head><body><table class="diff"><colgroup><col width="40"><col><col width="40"><col></colgroup><tr><td class="blob-num">1</td><td class="blob-code"><ul class="category-program" data="11"><li><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></td>
|
||||
<td class="blob-num">1</td><td class="blob-code"><ul class="category-program" data="21"><li><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num blob-num-empty empty-cell"></td><td class="blob-code blob-code-empty empty-cell"></td>
|
||||
<td class="blob-num blob-num-replacement">2</td><td class="blob-code blob-code-replacement"><ul class="category-program" data="21"><li><div class="patch insert" data="10"><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">world</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></div></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">2</td><td class="blob-code"><ul class="category-program" data="11"></ul></td>
|
||||
<td class="blob-num">3</td><td class="blob-code"><ul class="category-program" data="21"></ul></td>
|
||||
|
||||
</tr></table></body></html>
|
24
test/diffs/nested-insert.split.js
Normal file
24
test/diffs/nested-insert.split.js
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html><head><link rel="stylesheet" href="style.css"></head><body><table class="diff"><colgroup><col width="40"><col><col width="40"><col></colgroup><tr><td class="blob-num">1</td><td class="blob-code"><ul class="category-program" data="14"><li><ul class="category-if_statement" data="13">if (<li><span class="category-true" data="1">true</span></li>) <li><ul class="category-statement_block" data="11">{
|
||||
</ul></li></ul></li></ul></td>
|
||||
<td class="blob-num">1</td><td class="blob-code"><ul class="category-program" data="24"><li><ul class="category-if_statement" data="23">if (<li><span class="category-true" data="1">true</span></li>) <li><ul class="category-statement_block" data="21">{
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">2</td><td class="blob-code"><ul class="category-program" data="14"><li><ul class="category-if_statement" data="13"><li><ul class="category-statement_block" data="11"> <li><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
<td class="blob-num">2</td><td class="blob-code"><ul class="category-program" data="24"><li><ul class="category-if_statement" data="23"><li><ul class="category-statement_block" data="21"> <li><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num blob-num-empty empty-cell"></td><td class="blob-code blob-code-empty empty-cell"></td>
|
||||
<td class="blob-num blob-num-replacement">3</td><td class="blob-code blob-code-replacement"><ul class="category-program" data="24"><li><ul class="category-if_statement" data="23"><li><ul class="category-statement_block" data="21"> <li><div class="patch insert" data="10"><ul class="category-expression_statement" data="10"><li><ul class="category-function_call" data="9"><li><ul class="category-member_access" data="3"><li><span class="category-identifier" data="1">console</span></li>.<li><span class="category-identifier" data="1">log</span></li></ul></li>(<li><ul class="category-arguments" data="5"><li><ul class="category-string" data="4"><li><span class="category-string" data="1">'</span></li><li><span class="category-string" data="1">world</span></li><li><span class="category-string" data="1">'</span></li></ul></li></ul></li>)</ul></li>;</ul></div></li>
|
||||
</ul></li></ul></li></ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">3</td><td class="blob-code"><ul class="category-program" data="14"><li><ul class="category-if_statement" data="13"><li><ul class="category-statement_block" data="11">}</ul></li></ul></li>
|
||||
</ul></td>
|
||||
<td class="blob-num">4</td><td class="blob-code"><ul class="category-program" data="24"><li><ul class="category-if_statement" data="23"><li><ul class="category-statement_block" data="21">}</ul></li></ul></li>
|
||||
</ul></td>
|
||||
|
||||
</tr><tr><td class="blob-num">4</td><td class="blob-code"><ul class="category-program" data="14"></ul></td>
|
||||
<td class="blob-num">5</td><td class="blob-code"><ul class="category-program" data="24"></ul></td>
|
||||
|
||||
</tr></table></body></html>
|
@ -1 +1 @@
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,29],"categories":["program"],"children":[{"range":[0,28],"categories":["expression_statement"],"children":[{"range":[0,27],"categories":["FunctionCall"],"children":[{"range":[0,11],"categories":["member_access"],"children":[{"range":[0,7],"categories":["identifier"]},{"range":[8,11],"categories":["identifier"]}]},{"range":[12,26],"categories":["arguments"],"children":[{"range":[12,26],"categories":["StringLiteral"],"children":[{"range":[12,13],"categories":["StringLiteral"]},{"range":[13,18],"categories":["StringLiteral"]},{"range":[18,19],"categories":["StringLiteral"]},{"range":[20,25],"categories":["StringLiteral"]},{"range":[25,26],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[0,29],"hasChanges":false},{"number":1,"terms":[{"range":[0,29],"categories":["program"],"children":[{"range":[0,28],"categories":["expression_statement"],"children":[{"range":[0,27],"categories":["FunctionCall"],"children":[{"range":[0,11],"categories":["member_access"],"children":[{"range":[0,7],"categories":["identifier"]},{"range":[8,11],"categories":["identifier"]}]},{"range":[12,26],"categories":["arguments"],"children":[{"range":[12,26],"categories":["StringLiteral"],"children":[{"range":[12,13],"categories":["StringLiteral"]},{"range":[13,18],"categories":["StringLiteral"]},{"range":[18,19],"categories":["StringLiteral"]},{"range":[20,25],"categories":["StringLiteral"]},{"range":[25,26],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[0,29],"hasChanges":false}],[{"number":2,"terms":[{"range":[29,29],"categories":["program"],"children":[]}],"range":[29,29],"hasChanges":false},{"number":2,"terms":[{"range":[29,30],"categories":["program"],"children":[]}],"range":[29,30],"hasChanges":false}],[{"number":3,"terms":[{"range":[30,56],"categories":["program"],"children":[{"patch":"insert","range":[30,55],"categories":["expression_statement"],"children":[{"range":[30,54],"categories":["FunctionCall"],"children":[{"range":[30,41],"categories":["member_access"],"children":[{"range":[30,37],"categories":["identifier"]},{"range":[38,41],"categories":["identifier"]}]},{"range":[42,53],"categories":["arguments"],"children":[{"range":[42,53],"categories":["StringLiteral"],"children":[{"range":[42,43],"categories":["StringLiteral"]},{"range":[43,52],"categories":["StringLiteral"]},{"range":[52,53],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[30,56],"hasChanges":true}],[{"number":4,"terms":[{"range":[56,56],"categories":["program"],"children":[]}],"range":[56,56],"hasChanges":false}]],"oids":["",""],"paths":["test/diffs/newline-at-eof.A.js","test/diffs/newline-at-eof.B.js"]}
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,29],"category":"program","children":[{"range":[0,28],"category":"expression_statement","children":[{"range":[0,27],"category":"FunctionCall","children":[{"range":[0,11],"category":"member_access","children":[{"range":[0,7],"category":"identifier"},{"range":[8,11],"category":"identifier"}]},{"range":[12,26],"category":"arguments","children":[{"range":[12,26],"category":"StringLiteral","children":[{"range":[12,13],"category":"StringLiteral"},{"range":[13,18],"category":"StringLiteral"},{"range":[18,19],"category":"StringLiteral"},{"range":[20,25],"category":"StringLiteral"},{"range":[25,26],"category":"StringLiteral"}]}]}]}]}]}],"range":[0,29],"hasChanges":false},{"number":1,"terms":[{"range":[0,29],"category":"program","children":[{"range":[0,28],"category":"expression_statement","children":[{"range":[0,27],"category":"FunctionCall","children":[{"range":[0,11],"category":"member_access","children":[{"range":[0,7],"category":"identifier"},{"range":[8,11],"category":"identifier"}]},{"range":[12,26],"category":"arguments","children":[{"range":[12,26],"category":"StringLiteral","children":[{"range":[12,13],"category":"StringLiteral"},{"range":[13,18],"category":"StringLiteral"},{"range":[18,19],"category":"StringLiteral"},{"range":[20,25],"category":"StringLiteral"},{"range":[25,26],"category":"StringLiteral"}]}]}]}]}]}],"range":[0,29],"hasChanges":false}],[{"number":2,"terms":[{"range":[29,29],"category":"program","children":[]}],"range":[29,29],"hasChanges":false},{"number":2,"terms":[{"range":[29,30],"category":"program","children":[]}],"range":[29,30],"hasChanges":false}],[{"number":3,"terms":[{"range":[30,56],"category":"program","children":[{"patch":"insert","range":[30,55],"category":"expression_statement","children":[{"range":[30,54],"category":"FunctionCall","children":[{"range":[30,41],"category":"member_access","children":[{"range":[30,37],"category":"identifier"},{"range":[38,41],"category":"identifier"}]},{"range":[42,53],"category":"arguments","children":[{"range":[42,53],"category":"StringLiteral","children":[{"range":[42,43],"category":"StringLiteral"},{"range":[43,52],"category":"StringLiteral"},{"range":[52,53],"category":"StringLiteral"}]}]}]}]}]}],"range":[30,56],"hasChanges":true}],[{"number":4,"terms":[{"range":[56,56],"category":"program","children":[]}],"range":[56,56],"hasChanges":false}]],"oids":["",""],"paths":["test/diffs/newline-at-eof.A.js","test/diffs/newline-at-eof.B.js"]}
|
@ -1 +1 @@
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,28],"categories":["program"],"children":[{"range":[0,28],"categories":["expression_statement"],"children":[{"range":[0,27],"categories":["FunctionCall"],"children":[{"range":[0,11],"categories":["member_access"],"children":[{"range":[0,7],"categories":["identifier"]},{"range":[8,11],"categories":["identifier"]}]},{"range":[12,26],"categories":["arguments"],"children":[{"range":[12,26],"categories":["StringLiteral"],"children":[{"range":[12,13],"categories":["StringLiteral"]},{"range":[13,18],"categories":["StringLiteral"]},{"range":[18,19],"categories":["StringLiteral"]},{"range":[20,25],"categories":["StringLiteral"]},{"range":[25,26],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[0,28],"hasChanges":false},{"number":1,"terms":[{"range":[0,29],"categories":["program"],"children":[{"range":[0,28],"categories":["expression_statement"],"children":[{"range":[0,27],"categories":["FunctionCall"],"children":[{"range":[0,11],"categories":["member_access"],"children":[{"range":[0,7],"categories":["identifier"]},{"range":[8,11],"categories":["identifier"]}]},{"range":[12,26],"categories":["arguments"],"children":[{"range":[12,26],"categories":["StringLiteral"],"children":[{"range":[12,13],"categories":["StringLiteral"]},{"range":[13,18],"categories":["StringLiteral"]},{"range":[18,19],"categories":["StringLiteral"]},{"range":[20,25],"categories":["StringLiteral"]},{"range":[25,26],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[0,29],"hasChanges":false}],[{"number":2,"terms":[{"range":[29,30],"categories":["program"],"children":[]}],"range":[29,30],"hasChanges":false}],[{"number":3,"terms":[{"range":[30,55],"categories":["program"],"children":[{"patch":"insert","range":[30,55],"categories":["expression_statement"],"children":[{"range":[30,54],"categories":["FunctionCall"],"children":[{"range":[30,41],"categories":["member_access"],"children":[{"range":[30,37],"categories":["identifier"]},{"range":[38,41],"categories":["identifier"]}]},{"range":[42,53],"categories":["arguments"],"children":[{"range":[42,53],"categories":["StringLiteral"],"children":[{"range":[42,43],"categories":["StringLiteral"]},{"range":[43,52],"categories":["StringLiteral"]},{"range":[52,53],"categories":["StringLiteral"]}]}]}]}]}]}],"range":[30,55],"hasChanges":true}]],"oids":["",""],"paths":["test/diffs/no-newline-at-eof.A.js","test/diffs/no-newline-at-eof.B.js"]}
|
||||
{"rows":[[{"number":1,"terms":[{"range":[0,28],"category":"program","children":[{"range":[0,28],"category":"expression_statement","children":[{"range":[0,27],"category":"FunctionCall","children":[{"range":[0,11],"category":"member_access","children":[{"range":[0,7],"category":"identifier"},{"range":[8,11],"category":"identifier"}]},{"range":[12,26],"category":"arguments","children":[{"range":[12,26],"category":"StringLiteral","children":[{"range":[12,13],"category":"StringLiteral"},{"range":[13,18],"category":"StringLiteral"},{"range":[18,19],"category":"StringLiteral"},{"range":[20,25],"category":"StringLiteral"},{"range":[25,26],"category":"StringLiteral"}]}]}]}]}]}],"range":[0,28],"hasChanges":false},{"number":1,"terms":[{"range":[0,29],"category":"program","children":[{"range":[0,28],"category":"expression_statement","children":[{"range":[0,27],"category":"FunctionCall","children":[{"range":[0,11],"category":"member_access","children":[{"range":[0,7],"category":"identifier"},{"range":[8,11],"category":"identifier"}]},{"range":[12,26],"category":"arguments","children":[{"range":[12,26],"category":"StringLiteral","children":[{"range":[12,13],"category":"StringLiteral"},{"range":[13,18],"category":"StringLiteral"},{"range":[18,19],"category":"StringLiteral"},{"range":[20,25],"category":"StringLiteral"},{"range":[25,26],"category":"StringLiteral"}]}]}]}]}]}],"range":[0,29],"hasChanges":false}],[{"number":2,"terms":[{"range":[29,30],"category":"program","children":[]}],"range":[29,30],"hasChanges":false}],[{"number":3,"terms":[{"range":[30,55],"category":"program","children":[{"patch":"insert","range":[30,55],"category":"expression_statement","children":[{"range":[30,54],"category":"FunctionCall","children":[{"range":[30,41],"category":"member_access","children":[{"range":[30,37],"category":"identifier"},{"range":[38,41],"category":"identifier"}]},{"range":[42,53],"category":"arguments","children":[{"range":[42,53],"category":"StringLiteral","children":[{"range":[42,43],"category":"StringLiteral"},{"range":[43,52],"category":"StringLiteral"},{"range":[52,53],"category":"StringLiteral"}]}]}]}]}]}],"range":[30,55],"hasChanges":true}]],"oids":["",""],"paths":["test/diffs/no-newline-at-eof.A.js","test/diffs/no-newline-at-eof.B.js"]}
|
2
vendor/tree-sitter-parsers
vendored
2
vendor/tree-sitter-parsers
vendored
@ -1 +1 @@
|
||||
Subproject commit 40a3d5376f56e76f4530c3fa3a86f857e457c30f
|
||||
Subproject commit 36a5a0fc1ff4990f34842a27bc9c6cb0d181a50e
|
35
weekly/2015-06-01.md
Normal file
35
weekly/2015-06-01.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Planning Meeting June 1, 2016
|
||||
|
||||
## Observations
|
||||
|
||||
@robrix:
|
||||
|
||||
- Since Alignment Diff PR was merged, it offers a good chance to step back and look at what would be most valuable in support of Diff Summaries and the 90 day goals.
|
||||
- Want to triage Diff Summaries and if we should be reprioritizing staff shipping Semantic Diff now that Alignment is merged.
|
||||
|
||||
@joshvera:
|
||||
|
||||
- Reviewed open issues to prepare for planning meeting while evaluating how they fit into our milestones.
|
||||
- Thinking about how we can ensure resilience if Semantic Diff times out, or fails and having fall back to traditional diffing.
|
||||
- Also thinking about how to ensure Semantic Diff can render correctly.
|
||||
|
||||
## Focus Points
|
||||
|
||||
- Update semantic diff sha in github/github so semantic diff can be updated in github local development environments [PR](https://github.com/github/github/pull/56240)
|
||||
- Improving Diff Summaries (performance and structure)
|
||||
- Profiling and algorithm improvements to SES (Performance label)
|
||||
- Fix Ruby Parser
|
||||
- Unicorn Timeouts (algorithmic improvement, better fallback (should probably be driven by workflow tools))
|
||||
- Benchmarking
|
||||
- Other Features: detecting and rendering moves (as part of DotCom milestone)
|
||||
|
||||
## Plan
|
||||
|
||||
@robrix & @joshvera:
|
||||
|
||||
- Rob to set aside Benchmarking for now, focus on profiling (pair with Rick).
|
||||
- Josh and Rick to pair on Diff Summary performance while Rob is out.
|
||||
- Have Rick take a look at the Unicorn timeouts (involving some profiling to identify where things are slowest).
|
||||
- Rob on vacation starting Tuesday June 7th (for 10 days)
|
||||
- Schedule mini-summit for week of June 20th.
|
||||
- Longer term planning including solidifying a road map will be held at the mini-summit.
|
81
weekly/2016-05-31.md
Normal file
81
weekly/2016-05-31.md
Normal file
@ -0,0 +1,81 @@
|
||||
# May 31th, 2016
|
||||
|
||||
NB: On Tuesday this week since Monday was Memorial Day.
|
||||
|
||||
## Agenda
|
||||
|
||||
1. Retrospective on last week:
|
||||
- What went well?
|
||||
- What was challenging?
|
||||
- What did you learn?
|
||||
|
||||
|
||||
## What went well?
|
||||
|
||||
@joshvera:
|
||||
|
||||
- Diff summaries merged.
|
||||
- Introduced a new prelude.
|
||||
- Pairing with Rick.
|
||||
- Alignment!
|
||||
|
||||
@rewinfrey:
|
||||
|
||||
- Pairing with Josh.
|
||||
- Was at MoonConf and enjoyed the conference.
|
||||
- Almost finished with Haskell Tic Tac Toe.
|
||||
|
||||
@robrix:
|
||||
|
||||
- Alignment resolved!
|
||||
- Getting diff summaries merged.
|
||||
|
||||
|
||||
## What was challenging?
|
||||
|
||||
@joshvera:
|
||||
|
||||
- Didn't make as much progress on the structure of diff summaries as desired.
|
||||
|
||||
@rewinfrey:
|
||||
|
||||
- Continuing to adjust to the project.
|
||||
- Hit an edge case in Minimax that is tricky.
|
||||
|
||||
@robrix:
|
||||
|
||||
- Not sure why the line approach in alignment solved the problem.
|
||||
- In stack 1.1 you cannot rely on it rebuilding internal packages (must clean and rebuild semantic diff tool)
|
||||
|
||||
|
||||
## What did you learn?
|
||||
|
||||
@joshvera:
|
||||
|
||||
- Learned about the different types of preludes.
|
||||
- Learned about an extension in GHC 8.0 that introduces Applicative Do syntax, but is tricky to use in parallel computations because of the order of executation.
|
||||
|
||||
@rewinfrey:
|
||||
|
||||
- Learned about different recursion schemes (zygotmorphism
|
||||
- The transition from thinking about function application to thinking about function composition was an eye-opener.
|
||||
- Understanding a lot more about why certain shapes are advantageous.
|
||||
- http://livestream.com/accounts/16500216/events/5467460 (first video)
|
||||
- Greg Pfeil's talk: 02:24 Recursion Where FP Hits Bottom
|
||||
- Amar Shah's talk: 04:24 Point Free or Die
|
||||
- Ashley Powell's talk: 05:57 Negotiating Salary for Women in Tech
|
||||
- Patrick Thomson's talk: 06:25 Bracer: Transforming Real-World Languages with Coproducts and Recursion Schemes
|
||||
|
||||
@robrix:
|
||||
|
||||
- Learned a lot about GHCi debugging.
|
||||
- Remembered to question assumptions, specifically about alignment property tests that were throwing results off.
|
||||
- Using Arbitrary for purposes other than QuickCheck.
|
||||
|
||||
|
||||
## Other Items
|
||||
|
||||
@robrix:
|
||||
|
||||
- Planning this week about the timeline for staffshipping diff summaries.
|
||||
- Planning for a possible mini-summit in late June.
|
Loading…
Reference in New Issue
Block a user