1
1
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:
Rob Rix 2016-06-06 09:22:36 -04:00
commit bfc28ec4a6
41 changed files with 333 additions and 238 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeFamilies, TypeSynonymInstances #-}
module Diff where
import Prologue

View File

@ -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)

View File

@ -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 diffs 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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)) =

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
{-# LANGUAGE TypeFamilies, TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeFamilies, TypeSynonymInstances #-}
module Term where
import Prologue

View File

@ -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 weve 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

View File

@ -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))

View File

@ -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)

View File

@ -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 ++)

View File

@ -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

View File

@ -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

View File

@ -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 = []}]

View File

@ -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
View File

@ -0,0 +1,2 @@
if (a &&
b) {}

1
test/crashers/573.B.js Normal file
View File

@ -0,0 +1 @@
if (a && b) {}

View File

@ -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">&#39;</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">&#39;</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">&#39;</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">&#39;</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">&#39;</span></li><li><span class="category-string">world</span></li><li><span class="category-string">&#39;</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>

View File

@ -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">&#39;</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">&#39;</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">&#39;</span></li><li><span class="category-string">hello</span></li><li><span class="category-string">&#39;</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">&#39;</span></li><li><span class="category-string">world</span></li><li><span class="category-string">&#39;</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>

View File

@ -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"]}

View 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">&#39;</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">&#39;</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">&#39;</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">&#39;</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">&#39;</span></li><li><span class="category-string" data="1">world</span></li><li><span class="category-string" data="1">&#39;</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>

View 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">&#39;</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">&#39;</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">&#39;</span></li><li><span class="category-string" data="1">hello</span></li><li><span class="category-string" data="1">&#39;</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">&#39;</span></li><li><span class="category-string" data="1">world</span></li><li><span class="category-string" data="1">&#39;</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>

View File

@ -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"]}

View File

@ -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"]}

@ -1 +1 @@
Subproject commit 40a3d5376f56e76f4530c3fa3a86f857e457c30f
Subproject commit 36a5a0fc1ff4990f34842a27bc9c6cb0d181a50e

35
weekly/2015-06-01.md Normal file
View 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
View 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.