2017-02-16 03:13:34 +03:00
|
|
|
module Renderer.TOC (toc, diffTOC, JSONSummary(..), Summarizable(..), isErrorSummary) where
|
2017-01-23 19:22:51 +03:00
|
|
|
|
2017-01-23 22:12:05 +03:00
|
|
|
import Category as C
|
|
|
|
import Data.Aeson
|
|
|
|
import Data.Functor.Both hiding (fst, snd)
|
2017-01-24 00:59:47 +03:00
|
|
|
import qualified Data.Functor.Both as Both
|
2017-02-14 03:15:38 +03:00
|
|
|
import Data.Text (toLower)
|
2017-01-23 19:22:51 +03:00
|
|
|
import Data.Record
|
2017-01-23 22:12:05 +03:00
|
|
|
import Diff
|
2017-01-23 19:22:51 +03:00
|
|
|
import Info
|
2017-01-23 22:12:05 +03:00
|
|
|
import Prologue
|
2017-04-04 00:15:58 +03:00
|
|
|
import Renderer.Summary (Summaries(..))
|
2017-01-23 22:12:05 +03:00
|
|
|
import qualified Data.List as List
|
|
|
|
import qualified Data.Map as Map hiding (null)
|
2017-02-10 01:10:16 +03:00
|
|
|
import Source hiding (null)
|
2017-01-23 22:12:05 +03:00
|
|
|
import Syntax as S
|
|
|
|
import Term
|
2017-01-24 01:26:43 +03:00
|
|
|
import Patch
|
2017-01-23 20:16:59 +03:00
|
|
|
|
2017-01-24 22:16:22 +03:00
|
|
|
data JSONSummary = JSONSummary { info :: Summarizable }
|
2017-01-23 23:58:20 +03:00
|
|
|
| ErrorSummary { error :: Text, errorSpan :: SourceSpan }
|
2017-01-23 22:12:05 +03:00
|
|
|
deriving (Generic, Eq, Show)
|
|
|
|
|
2017-01-24 00:33:07 +03:00
|
|
|
instance ToJSON JSONSummary where
|
2017-01-24 22:16:22 +03:00
|
|
|
toJSON JSONSummary{..} = object $ case info of
|
2017-02-18 01:57:29 +03:00
|
|
|
InSummarizable{..} -> [ "changeType" .= ("modified" :: Text), "category" .= toCategoryName parentCategory, "term" .= parentTermName, "span" .= parentSourceSpan ]
|
|
|
|
Summarizable{..} -> [ "changeType" .= summarizableChangeType, "category" .= toCategoryName summarizableCategory, "term" .= summarizableTermName, "span" .= summarizableSourceSpan ]
|
2017-01-23 23:58:20 +03:00
|
|
|
toJSON ErrorSummary{..} = object [ "error" .= error, "span" .= errorSpan ]
|
2017-01-23 22:12:05 +03:00
|
|
|
|
2017-01-24 00:33:07 +03:00
|
|
|
isErrorSummary :: JSONSummary -> Bool
|
2017-01-23 22:12:05 +03:00
|
|
|
isErrorSummary ErrorSummary{} = True
|
|
|
|
isErrorSummary _ = False
|
2017-01-23 20:16:59 +03:00
|
|
|
|
2017-05-08 23:00:29 +03:00
|
|
|
data DiffInfo
|
|
|
|
= LeafInfo
|
|
|
|
{ leafCategory :: Category
|
|
|
|
, termName :: Text
|
|
|
|
, leafSourceSpan :: SourceSpan
|
|
|
|
}
|
|
|
|
| ErrorInfo
|
|
|
|
{ infoSpan :: SourceSpan
|
|
|
|
, termName :: Text
|
|
|
|
}
|
|
|
|
deriving (Eq, Show)
|
2017-01-23 20:16:59 +03:00
|
|
|
|
|
|
|
data TOCSummary a = TOCSummary {
|
2017-01-25 21:45:56 +03:00
|
|
|
summaryPatch :: Patch a,
|
2017-05-08 18:31:30 +03:00
|
|
|
parentInfo :: Maybe Summarizable
|
2017-01-24 22:16:22 +03:00
|
|
|
} deriving (Eq, Functor, Show, Generic)
|
2017-01-23 20:16:59 +03:00
|
|
|
|
2017-05-08 22:11:12 +03:00
|
|
|
data Summarizable
|
|
|
|
= Summarizable
|
|
|
|
{ summarizableCategory :: Category
|
|
|
|
, summarizableTermName :: Text
|
|
|
|
, summarizableSourceSpan :: SourceSpan
|
|
|
|
, summarizableChangeType :: Text
|
|
|
|
}
|
|
|
|
| InSummarizable
|
|
|
|
{ parentCategory :: Category
|
|
|
|
, parentTermName :: Text
|
|
|
|
, parentSourceSpan :: SourceSpan
|
|
|
|
}
|
2017-01-24 00:33:07 +03:00
|
|
|
deriving (Eq, Show)
|
|
|
|
|
2017-04-04 00:15:58 +03:00
|
|
|
toc :: HasDefaultFields fields => Both SourceBlob -> Diff (Syntax Text) (Record fields) -> Summaries
|
|
|
|
toc blobs diff = Summaries changes errors
|
2017-01-23 22:12:05 +03:00
|
|
|
where
|
|
|
|
changes = if null changes' then mempty else Map.singleton summaryKey (toJSON <$> changes')
|
|
|
|
errors = if null errors' then mempty else Map.singleton summaryKey (toJSON <$> errors')
|
|
|
|
(errors', changes') = List.partition isErrorSummary summaries
|
|
|
|
summaries = diffTOC blobs diff
|
2017-01-23 20:16:59 +03:00
|
|
|
|
2017-05-08 20:21:40 +03:00
|
|
|
summaryKey = toS $ case runJoin (path <$> blobs) of
|
|
|
|
(before, after) | null before -> after
|
|
|
|
| null after -> before
|
|
|
|
| before == after -> after
|
|
|
|
| otherwise -> before <> " -> " <> after
|
2017-03-31 23:48:06 +03:00
|
|
|
|
2017-05-08 18:36:45 +03:00
|
|
|
diffTOC :: HasDefaultFields fields => Both SourceBlob -> Diff (Syntax Text) (Record fields) -> [JSONSummary]
|
2017-02-14 04:30:33 +03:00
|
|
|
diffTOC blobs diff = removeDupes (diffToTOCSummaries (source <$> blobs) diff) >>= toJSONSummaries
|
2017-01-23 22:12:05 +03:00
|
|
|
where
|
2017-01-24 20:13:59 +03:00
|
|
|
removeDupes :: [TOCSummary DiffInfo] -> [TOCSummary DiffInfo]
|
2017-02-14 22:29:24 +03:00
|
|
|
removeDupes = foldl' go []
|
|
|
|
where
|
|
|
|
go xs x | (_, _ : _) <- find exactMatch x xs = xs
|
|
|
|
| (front, existingItem : back) <- find similarMatch x xs =
|
|
|
|
let
|
2017-05-08 18:31:30 +03:00
|
|
|
Just (Summarizable category name sourceSpan _) = parentInfo existingItem
|
|
|
|
replacement = x { parentInfo = Just $ Summarizable category name sourceSpan "modified" }
|
2017-02-14 22:29:24 +03:00
|
|
|
in
|
|
|
|
front <> (replacement : back)
|
|
|
|
| otherwise = xs <> [x]
|
|
|
|
find p x = List.break (p x)
|
|
|
|
exactMatch a b = parentInfo a == parentInfo b
|
|
|
|
similarMatch a b = case (parentInfo a, parentInfo b) of
|
2017-05-08 18:31:30 +03:00
|
|
|
(Just (Summarizable catA nameA _ _), Just (Summarizable catB nameB _ _)) -> catA == catB && toLower nameA == toLower nameB
|
2017-02-14 22:29:24 +03:00
|
|
|
(_, _) -> False
|
2017-01-24 20:13:59 +03:00
|
|
|
|
2017-05-08 18:36:45 +03:00
|
|
|
diffToTOCSummaries :: HasDefaultFields fields => Both Source -> Diff (Syntax Text) (Record fields) -> [TOCSummary DiffInfo]
|
2017-05-08 22:20:22 +03:00
|
|
|
diffToTOCSummaries sources = para $ \diff -> case diff of
|
2017-05-08 23:41:10 +03:00
|
|
|
Free (annotations :< syntax) -> foldMap (fmap (contextualize (Both.snd sources) (Both.snd annotations :< fmap fst syntax)) . snd) diff
|
2017-05-08 23:00:29 +03:00
|
|
|
Pure patch -> fmap toTOCSummaries (sequenceA (runBothWith mapPatch (toInfo <$> sources) patch))
|
2017-05-08 22:47:51 +03:00
|
|
|
|
2017-05-08 23:00:29 +03:00
|
|
|
toInfo :: HasDefaultFields fields => Source -> Term (Syntax Text) (Record fields) -> [DiffInfo]
|
2017-05-08 22:47:51 +03:00
|
|
|
toInfo source = para $ \ (annotation :< syntax) -> let termName = toTermName source (cofree (annotation :< (fst <$> syntax))) in case syntax of
|
2017-05-08 23:00:29 +03:00
|
|
|
S.ParseError _ -> [ErrorInfo (sourceSpan annotation) termName]
|
|
|
|
S.Indexed children -> children >>= snd
|
|
|
|
S.Fixed children -> children >>= snd
|
|
|
|
S.Commented cs leaf -> (cs <> maybeToList leaf) >>= snd
|
|
|
|
S.AnonymousFunction _ _ -> [LeafInfo C.AnonymousFunction termName (sourceSpan annotation)]
|
|
|
|
_ -> [LeafInfo (category annotation) termName (sourceSpan annotation)]
|
2017-05-08 22:47:51 +03:00
|
|
|
|
|
|
|
toTOCSummaries patch = case afterOrBefore patch of
|
2017-05-08 23:00:29 +03:00
|
|
|
ErrorInfo{..} -> TOCSummary patch Nothing
|
|
|
|
LeafInfo{..} -> TOCSummary patch $ case leafCategory of
|
2017-05-08 22:47:51 +03:00
|
|
|
C.Function -> Just $ Summarizable leafCategory termName leafSourceSpan (patchType patch)
|
|
|
|
C.Method -> Just $ Summarizable leafCategory termName leafSourceSpan (patchType patch)
|
|
|
|
C.SingletonMethod -> Just $ Summarizable leafCategory termName leafSourceSpan (patchType patch)
|
|
|
|
_ -> Nothing
|
2017-01-24 22:16:22 +03:00
|
|
|
|
2017-05-08 18:16:37 +03:00
|
|
|
contextualize source (annotation :< syntax) summary
|
2017-05-08 18:31:30 +03:00
|
|
|
| Nothing <- parentInfo summary
|
2017-05-08 18:16:37 +03:00
|
|
|
, isSummarizable syntax
|
2017-05-08 18:31:30 +03:00
|
|
|
, Just terms <- traverse afterTerm syntax = summary { parentInfo = Just (InSummarizable (category annotation) (toTermName source (cofree (annotation :< terms))) (sourceSpan annotation)) }
|
2017-05-08 18:16:37 +03:00
|
|
|
| otherwise = summary
|
|
|
|
|
|
|
|
isSummarizable S.Method{} = True
|
|
|
|
isSummarizable S.Function{} = True
|
|
|
|
isSummarizable _ = False
|
|
|
|
|
2017-01-24 22:16:22 +03:00
|
|
|
toJSONSummaries :: TOCSummary DiffInfo -> [JSONSummary]
|
2017-05-05 22:26:26 +03:00
|
|
|
toJSONSummaries TOCSummary{..} = toJSONSummaries' (afterOrBefore summaryPatch)
|
2017-01-24 22:16:22 +03:00
|
|
|
where
|
2017-03-28 22:32:45 +03:00
|
|
|
toJSONSummaries' diffInfo = case diffInfo of
|
2017-05-08 18:32:25 +03:00
|
|
|
ErrorInfo{..} -> [ErrorSummary termName infoSpan]
|
2017-05-08 18:31:30 +03:00
|
|
|
LeafInfo{..} -> maybe [] (pure . JSONSummary) parentInfo
|
2017-01-23 22:12:05 +03:00
|
|
|
|
2017-05-08 18:37:10 +03:00
|
|
|
toTermName :: HasDefaultFields fields => Source -> Term (Syntax Text) (Record fields) -> Text
|
2017-05-08 18:22:04 +03:00
|
|
|
toTermName source = para $ \ (annotation :< syntax) -> case syntax of
|
|
|
|
S.Function (_, identifier) _ _ -> identifier
|
|
|
|
S.Method _ (_, identifier) Nothing _ _ -> identifier
|
2017-05-08 18:24:25 +03:00
|
|
|
S.Method _ (_, identifier) (Just (receiver, receiverSource)) _ _
|
|
|
|
| S.Indexed [receiverParams] <- unwrap receiver
|
|
|
|
, S.ParameterDecl (Just ty) _ <- unwrap receiverParams -> "(" <> toTermName source ty <> ") " <> identifier
|
|
|
|
| otherwise -> receiverSource <> "." <> identifier
|
2017-05-08 18:22:04 +03:00
|
|
|
_ -> toText (Source.slice (byteRange annotation) source)
|
2017-02-18 01:57:29 +03:00
|
|
|
|
|
|
|
-- The user-facing category name
|
|
|
|
toCategoryName :: Category -> Text
|
2017-03-28 22:32:45 +03:00
|
|
|
toCategoryName category = case category of
|
2017-02-23 07:05:20 +03:00
|
|
|
C.SingletonMethod -> "Method"
|
2017-02-18 01:57:29 +03:00
|
|
|
c -> show c
|