1
1
mirror of https://github.com/github/semantic.git synced 2024-12-27 08:54:14 +03:00
semantic/src/Renderer/TOC.hs

153 lines
6.8 KiB
Haskell
Raw Normal View History

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
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
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)
import Source hiding (null)
2017-01-23 22:12:05 +03:00
import Syntax as S
import Term
import Patch
2017-01-23 20:16:59 +03:00
data JSONSummary = JSONSummary { info :: Summarizable }
| ErrorSummary { error :: Text, errorSpan :: SourceSpan }
2017-01-23 22:12:05 +03:00
deriving (Generic, Eq, Show)
instance ToJSON JSONSummary where
toJSON JSONSummary{..} = object $ case info of
InSummarizable{..} -> [ "changeType" .= ("modified" :: Text), "category" .= toCategoryName parentCategory, "term" .= parentTermName, "span" .= parentSourceSpan ]
Summarizable{..} -> [ "changeType" .= summarizableChangeType, "category" .= toCategoryName summarizableCategory, "term" .= summarizableTermName, "span" .= summarizableSourceSpan ]
toJSON ErrorSummary{..} = object [ "error" .= error, "span" .= errorSpan ]
2017-01-23 22:12:05 +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
} deriving (Eq, Functor, Show, Generic)
2017-01-23 20:16:59 +03:00
data Summarizable
= Summarizable
{ summarizableCategory :: Category
, summarizableTermName :: Text
, summarizableSourceSpan :: SourceSpan
, summarizableChangeType :: Text
}
| InSummarizable
{ parentCategory :: Category
, parentTermName :: Text
, parentSourceSpan :: SourceSpan
}
deriving (Eq, Show)
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
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]
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]
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" }
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
(_, _) -> 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]
diffToTOCSummaries sources = para $ \diff -> case diff of
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 23:00:29 +03:00
toInfo :: HasDefaultFields fields => Source -> Term (Syntax Text) (Record fields) -> [DiffInfo]
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)]
toTOCSummaries patch = case afterOrBefore patch of
2017-05-08 23:00:29 +03:00
ErrorInfo{..} -> TOCSummary patch Nothing
LeafInfo{..} -> TOCSummary patch $ case leafCategory of
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
contextualize source (annotation :< syntax) summary
2017-05-08 18:31:30 +03:00
| Nothing <- parentInfo summary
, 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)) }
| otherwise = summary
isSummarizable S.Method{} = True
isSummarizable S.Function{} = True
isSummarizable _ = False
toJSONSummaries :: TOCSummary DiffInfo -> [JSONSummary]
2017-05-05 22:26:26 +03:00
toJSONSummaries TOCSummary{..} = toJSONSummaries' (afterOrBefore summaryPatch)
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)
-- 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"
c -> show c