mirror of
https://github.com/github/semantic.git
synced 2024-12-26 00:12:29 +03:00
🔥 the summary renderer.
This commit is contained in:
parent
9793809b7f
commit
7f72974e27
@ -61,7 +61,6 @@ library
|
|||||||
, Renderer
|
, Renderer
|
||||||
, Renderer.JSON
|
, Renderer.JSON
|
||||||
, Renderer.Patch
|
, Renderer.Patch
|
||||||
, Renderer.Summary
|
|
||||||
, Renderer.SExpression
|
, Renderer.SExpression
|
||||||
, Renderer.TOC
|
, Renderer.TOC
|
||||||
, RWS
|
, RWS
|
||||||
@ -157,7 +156,6 @@ test-suite test
|
|||||||
, DiffSpec
|
, DiffSpec
|
||||||
, SemanticSpec
|
, SemanticSpec
|
||||||
, SemanticCmdLineSpec
|
, SemanticCmdLineSpec
|
||||||
, SummarySpec
|
|
||||||
, GitmonClientSpec
|
, GitmonClientSpec
|
||||||
, InterpreterSpec
|
, InterpreterSpec
|
||||||
, PatchOutputSpec
|
, PatchOutputSpec
|
||||||
|
@ -46,9 +46,6 @@ patchDiff = DiffArguments PatchRenderer identityDecorator
|
|||||||
jsonDiff :: DiffArguments'
|
jsonDiff :: DiffArguments'
|
||||||
jsonDiff = DiffArguments JSONDiffRenderer identityDecorator
|
jsonDiff = DiffArguments JSONDiffRenderer identityDecorator
|
||||||
|
|
||||||
summaryDiff :: DiffArguments'
|
|
||||||
summaryDiff = DiffArguments SummaryRenderer identityDecorator
|
|
||||||
|
|
||||||
sExpressionDiff :: DiffArguments'
|
sExpressionDiff :: DiffArguments'
|
||||||
sExpressionDiff = DiffArguments (SExpressionDiffRenderer TreeOnly) identityDecorator
|
sExpressionDiff = DiffArguments (SExpressionDiffRenderer TreeOnly) identityDecorator
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import Prologue
|
|||||||
import Renderer.JSON as R
|
import Renderer.JSON as R
|
||||||
import Renderer.Patch as R
|
import Renderer.Patch as R
|
||||||
import Renderer.SExpression as R
|
import Renderer.SExpression as R
|
||||||
import Renderer.Summary as R
|
|
||||||
import Renderer.TOC as R
|
import Renderer.TOC as R
|
||||||
import Source (SourceBlob(..), Source)
|
import Source (SourceBlob(..), Source)
|
||||||
import Syntax as S
|
import Syntax as S
|
||||||
@ -35,7 +34,6 @@ import Term
|
|||||||
data DiffRenderer fields output where
|
data DiffRenderer fields output where
|
||||||
PatchRenderer :: HasField fields Range => DiffRenderer fields File
|
PatchRenderer :: HasField fields Range => DiffRenderer fields File
|
||||||
JSONDiffRenderer :: (ToJSONFields (Record fields), HasField fields Range) => DiffRenderer fields (Map Text Value)
|
JSONDiffRenderer :: (ToJSONFields (Record fields), HasField fields Range) => DiffRenderer fields (Map Text Value)
|
||||||
SummaryRenderer :: HasDefaultFields fields => DiffRenderer fields Summaries
|
|
||||||
SExpressionDiffRenderer :: (HasField fields Category, HasField fields SourceSpan) => SExpressionFormat -> DiffRenderer fields ByteString
|
SExpressionDiffRenderer :: (HasField fields Category, HasField fields SourceSpan) => SExpressionFormat -> DiffRenderer fields ByteString
|
||||||
ToCRenderer :: (HasField fields Category, HasField fields (Maybe Declaration), HasField fields SourceSpan) => DiffRenderer fields Summaries
|
ToCRenderer :: (HasField fields Category, HasField fields (Maybe Declaration), HasField fields SourceSpan) => DiffRenderer fields Summaries
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ resolveDiffRenderer :: (Monoid output, StringConv output ByteString) => DiffRend
|
|||||||
resolveDiffRenderer renderer = case renderer of
|
resolveDiffRenderer renderer = case renderer of
|
||||||
PatchRenderer -> (File .) . R.patch
|
PatchRenderer -> (File .) . R.patch
|
||||||
JSONDiffRenderer -> R.json
|
JSONDiffRenderer -> R.json
|
||||||
SummaryRenderer -> R.summary
|
|
||||||
SExpressionDiffRenderer format -> R.sExpression format
|
SExpressionDiffRenderer format -> R.sExpression format
|
||||||
ToCRenderer -> R.toc
|
ToCRenderer -> R.toc
|
||||||
|
|
||||||
@ -102,7 +99,6 @@ instance StringConv File ByteString where
|
|||||||
instance Show (DiffRenderer fields output) where
|
instance Show (DiffRenderer fields output) where
|
||||||
showsPrec _ PatchRenderer = showString "PatchRenderer"
|
showsPrec _ PatchRenderer = showString "PatchRenderer"
|
||||||
showsPrec _ JSONDiffRenderer = showString "JSONDiffRenderer"
|
showsPrec _ JSONDiffRenderer = showString "JSONDiffRenderer"
|
||||||
showsPrec _ SummaryRenderer = showString "SummaryRenderer"
|
|
||||||
showsPrec d (SExpressionDiffRenderer format) = showsUnaryWith showsPrec "SExpressionDiffRenderer" d format
|
showsPrec d (SExpressionDiffRenderer format) = showsUnaryWith showsPrec "SExpressionDiffRenderer" d format
|
||||||
showsPrec _ ToCRenderer = showString "ToCRenderer"
|
showsPrec _ ToCRenderer = showString "ToCRenderer"
|
||||||
|
|
||||||
|
@ -1,548 +0,0 @@
|
|||||||
{-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables #-}
|
|
||||||
{-# OPTIONS_GHC -Wno-deprecations #-}
|
|
||||||
-- Disabling deprecation warnings due to pattern match against RescueModifier.
|
|
||||||
module Renderer.Summary (Summaries(..), summary, diffSummaries, DiffSummary(..), DiffInfo(..), diffToDiffSummaries, isBranchInfo, isErrorSummary, JSONSummary(..)) where
|
|
||||||
|
|
||||||
import Prologue
|
|
||||||
import Diff
|
|
||||||
import Patch
|
|
||||||
import Term
|
|
||||||
import Info (HasDefaultFields, category, byteRange)
|
|
||||||
import Range
|
|
||||||
import Syntax as S
|
|
||||||
import Category as C
|
|
||||||
import Data.Functor.Both hiding (fst, snd)
|
|
||||||
import qualified Data.Functor.Both as Both
|
|
||||||
import Data.Functor.Listable
|
|
||||||
import Data.List.NonEmpty (nonEmpty)
|
|
||||||
import qualified Data.Text as Text
|
|
||||||
import Data.Text.Listable
|
|
||||||
import Data.Record
|
|
||||||
import Data.These
|
|
||||||
import Text.PrettyPrint.Leijen.Text ((<+>), squotes, space, string, Doc, punctuate, pretty, hsep)
|
|
||||||
import qualified Text.PrettyPrint.Leijen.Text as P
|
|
||||||
import Data.Aeson
|
|
||||||
import SourceSpan
|
|
||||||
import Source hiding (null)
|
|
||||||
import qualified Data.Map as Map
|
|
||||||
import qualified Data.List as List
|
|
||||||
|
|
||||||
data Summaries = Summaries { changes, errors :: !(Map Text [Value]) }
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
instance Monoid Summaries where
|
|
||||||
mempty = Summaries mempty mempty
|
|
||||||
mappend (Summaries c1 e1) (Summaries c2 e2) = Summaries (Map.unionWith (<>) c1 c2) (Map.unionWith (<>) e1 e2)
|
|
||||||
|
|
||||||
instance StringConv Summaries ByteString where
|
|
||||||
strConv _ = toS . (<> "\n") . encode
|
|
||||||
|
|
||||||
instance ToJSON Summaries where
|
|
||||||
toJSON Summaries{..} = object [ "changes" .= changes, "errors" .= errors ]
|
|
||||||
|
|
||||||
data Annotatable a = Annotatable a | Unannotatable a
|
|
||||||
|
|
||||||
annotatable :: SyntaxTerm leaf fields -> Annotatable (SyntaxTerm leaf fields)
|
|
||||||
annotatable term = isAnnotatable (unwrap term) term
|
|
||||||
where isAnnotatable syntax = case syntax of
|
|
||||||
S.Class{} -> Annotatable
|
|
||||||
S.Method{} -> Annotatable
|
|
||||||
S.Function{} -> Annotatable
|
|
||||||
S.Module{} -> Annotatable
|
|
||||||
S.Namespace{} -> Annotatable
|
|
||||||
S.Interface{} -> Annotatable
|
|
||||||
_ -> Unannotatable
|
|
||||||
|
|
||||||
data Identifiable a = Identifiable a | Unidentifiable a
|
|
||||||
|
|
||||||
identifiable :: SyntaxTerm leaf fields -> Identifiable (SyntaxTerm leaf fields)
|
|
||||||
identifiable term = isIdentifiable (unwrap term) term
|
|
||||||
where isIdentifiable syntax = case syntax of
|
|
||||||
S.FunctionCall{} -> Identifiable
|
|
||||||
S.MethodCall{} -> Identifiable
|
|
||||||
S.Function{} -> Identifiable
|
|
||||||
S.Assignment{} -> Identifiable
|
|
||||||
S.OperatorAssignment{} -> Identifiable
|
|
||||||
S.VarAssignment{} -> Identifiable
|
|
||||||
S.SubscriptAccess{} -> Identifiable
|
|
||||||
S.Module{} -> Identifiable
|
|
||||||
S.Namespace{} -> Identifiable
|
|
||||||
S.Interface{} -> Identifiable
|
|
||||||
S.Class{} -> Identifiable
|
|
||||||
S.Method{} -> Identifiable
|
|
||||||
S.Leaf{} -> Identifiable
|
|
||||||
S.DoWhile{} -> Identifiable
|
|
||||||
S.Import{} -> Identifiable
|
|
||||||
S.Export{} -> Identifiable
|
|
||||||
S.Ternary{} -> Identifiable
|
|
||||||
S.If{} -> Identifiable
|
|
||||||
S.Try{} -> Identifiable
|
|
||||||
S.Switch{} -> Identifiable
|
|
||||||
S.Rescue{} -> Identifiable
|
|
||||||
S.Pair{} -> Identifiable
|
|
||||||
S.Array ty _ -> maybe Unidentifiable (const Identifiable) ty
|
|
||||||
S.Object ty _ -> maybe Unidentifiable (const Identifiable) ty
|
|
||||||
S.BlockStatement{} -> Identifiable
|
|
||||||
S.TypeDecl{} -> Identifiable
|
|
||||||
S.Ty{} -> Identifiable
|
|
||||||
_ -> Unidentifiable
|
|
||||||
|
|
||||||
data JSONSummary info span = JSONSummary { info :: info, span :: span }
|
|
||||||
| ErrorSummary { info :: info, span :: span }
|
|
||||||
deriving (Generic, Eq, Show)
|
|
||||||
|
|
||||||
instance (ToJSON info, ToJSON span) => ToJSON (JSONSummary info span) where
|
|
||||||
toJSON JSONSummary{..} = object [ "summary" .= info, "span" .= span ]
|
|
||||||
toJSON ErrorSummary{..} = object [ "summary" .= info, "span" .= span ]
|
|
||||||
|
|
||||||
isErrorSummary :: JSONSummary info span -> Bool
|
|
||||||
isErrorSummary ErrorSummary{} = True
|
|
||||||
isErrorSummary _ = False
|
|
||||||
|
|
||||||
data DiffInfo = LeafInfo { leafCategory :: Category, termName :: Text, sourceSpan :: SourceSpan }
|
|
||||||
| BranchInfo { branches :: [ DiffInfo ], branchCategory :: Category, branchType :: Branch }
|
|
||||||
| ErrorInfo { errorSpan :: SourceSpan, termName :: Text }
|
|
||||||
| HideInfo -- Hide/Strip from summary output entirely.
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data Branch = BIndexed | BFixed | BCommented | BIf deriving (Show, Eq, Generic)
|
|
||||||
|
|
||||||
data DiffSummary a = DiffSummary {
|
|
||||||
diffSummaryPatch :: Patch a,
|
|
||||||
parentAnnotation :: [Either (Category, Text) (Category, Text)]
|
|
||||||
} deriving (Eq, Functor, Show, Generic)
|
|
||||||
|
|
||||||
summary :: HasDefaultFields fields => Both SourceBlob -> Diff (Syntax Text) (Record fields) -> Summaries
|
|
||||||
summary blobs diff = Summaries changes errors
|
|
||||||
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
|
|
||||||
summaryKey = toSummaryKey (path <$> blobs)
|
|
||||||
summaries = diffSummaries blobs diff
|
|
||||||
|
|
||||||
-- Returns a key representing the filename. If the filenames are different,
|
|
||||||
-- return 'before -> after'.
|
|
||||||
toSummaryKey :: Both FilePath -> Text
|
|
||||||
toSummaryKey = runBothWith $ \before after ->
|
|
||||||
toS $ case (before, after) of
|
|
||||||
("", after) -> after
|
|
||||||
(before, "") -> before
|
|
||||||
(before, after) | before == after -> after
|
|
||||||
(before, after) | not (null before) && not (null after) -> before <> " -> " <> after
|
|
||||||
(_, _) -> mempty
|
|
||||||
|
|
||||||
-- Returns a list of diff summary texts given two source blobs and a diff.
|
|
||||||
diffSummaries :: (StringConv leaf Text, HasDefaultFields fields) => Both SourceBlob -> SyntaxDiff leaf fields -> [JSONSummary Text SourceSpans]
|
|
||||||
diffSummaries blobs diff = summaryToTexts =<< diffToDiffSummaries (source <$> blobs) diff
|
|
||||||
|
|
||||||
-- Takes a 'DiffSummary DiffInfo' and returns a list of JSON Summaries whose text summaries represent the LeafInfo summaries of the 'DiffSummary'.
|
|
||||||
summaryToTexts :: DiffSummary DiffInfo -> [JSONSummary Text SourceSpans]
|
|
||||||
summaryToTexts DiffSummary{..} = appendParentContexts <$> jsonDocSummaries diffSummaryPatch
|
|
||||||
where appendParentContexts jsonSummary =
|
|
||||||
jsonSummary { info = show $ info jsonSummary <+> parentContexts parentAnnotation }
|
|
||||||
|
|
||||||
-- Returns a list of 'DiffSummary' given two source blobs and a diff.
|
|
||||||
diffToDiffSummaries :: (StringConv leaf Text, HasDefaultFields fields) => Both Source -> SyntaxDiff leaf fields -> [DiffSummary DiffInfo]
|
|
||||||
diffToDiffSummaries sources = para $ \diff ->
|
|
||||||
let
|
|
||||||
diff' = free (Prologue.fst <$> diff)
|
|
||||||
annotateWithCategory :: [DiffSummary DiffInfo] -> [DiffSummary DiffInfo]
|
|
||||||
annotateWithCategory children = case (beforeTerm diff', afterTerm diff') of
|
|
||||||
(_, Just diff'') -> appendSummary (Both.snd sources) diff'' <$> children
|
|
||||||
(Just diff'', _) -> appendSummary (Both.fst sources) diff'' <$> children
|
|
||||||
(Nothing, Nothing) -> []
|
|
||||||
in case diff of
|
|
||||||
-- Skip comments and leaves since they don't have any changes
|
|
||||||
(Free (_ :< syntax)) -> annotateWithCategory (toList syntax >>= snd)
|
|
||||||
(Pure patch) -> [ DiffSummary (mapPatch (termToDiffInfo beforeSource) (termToDiffInfo afterSource) patch) [] ]
|
|
||||||
where
|
|
||||||
(beforeSource, afterSource) = runJoin sources
|
|
||||||
|
|
||||||
-- Flattens a patch of diff infos into a list of docs, one for every 'LeafInfo' or `ErrorInfo` it contains.
|
|
||||||
jsonDocSummaries :: Patch DiffInfo -> [JSONSummary Doc SourceSpans]
|
|
||||||
jsonDocSummaries patch = case patch of
|
|
||||||
Replace i1 i2 -> zipWith (\a b ->
|
|
||||||
JSONSummary
|
|
||||||
{
|
|
||||||
info = info (prefixWithPatch patch This a) <+> "with" <+> info b
|
|
||||||
, span = SourceSpans $ These (span a) (span b)
|
|
||||||
}) (toLeafInfos i1) (toLeafInfos i2)
|
|
||||||
Insert info -> prefixWithPatch patch That <$> toLeafInfos info
|
|
||||||
Delete info -> prefixWithPatch patch This <$> toLeafInfos info
|
|
||||||
|
|
||||||
-- Prefixes a given doc with the type of patch it represents.
|
|
||||||
prefixWithPatch :: Patch DiffInfo -> (SourceSpan -> These SourceSpan SourceSpan) -> JSONSummary Doc SourceSpan -> JSONSummary Doc SourceSpans
|
|
||||||
prefixWithPatch patch constructor = prefixWithThe (patchToPrefix patch)
|
|
||||||
where
|
|
||||||
prefixWithThe prefix jsonSummary = jsonSummary
|
|
||||||
{
|
|
||||||
info = prefix <+> info jsonSummary
|
|
||||||
, span = SourceSpans $ constructor (span jsonSummary)
|
|
||||||
}
|
|
||||||
patchToPrefix patch = case patch of
|
|
||||||
(Replace _ _) -> "Replaced"
|
|
||||||
(Insert _) -> "Added"
|
|
||||||
(Delete _) -> "Deleted"
|
|
||||||
|
|
||||||
toLeafInfos :: DiffInfo -> [JSONSummary Doc SourceSpan]
|
|
||||||
toLeafInfos err@ErrorInfo{..} = pure $ ErrorSummary (pretty err) errorSpan
|
|
||||||
toLeafInfos BranchInfo{..} = branches >>= toLeafInfos
|
|
||||||
toLeafInfos HideInfo = []
|
|
||||||
toLeafInfos LeafInfo{..} = pure $ JSONSummary (summary leafCategory termName) sourceSpan
|
|
||||||
where
|
|
||||||
summary :: Category -> Text -> Doc
|
|
||||||
summary category termName = case category of
|
|
||||||
C.NumberLiteral -> squotes $ toDoc termName
|
|
||||||
C.IntegerLiteral -> squotes $ toDoc termName
|
|
||||||
C.Boolean -> squotes $ toDoc termName
|
|
||||||
C.StringLiteral -> termAndCategoryName
|
|
||||||
C.Export -> termAndCategoryName
|
|
||||||
C.Import -> termAndCategoryName
|
|
||||||
C.Subshell -> termAndCategoryName
|
|
||||||
C.AnonymousFunction -> "an" <+> toDoc termName <+> "function"
|
|
||||||
C.Begin -> categoryName'
|
|
||||||
C.Select -> categoryName'
|
|
||||||
C.Else -> categoryName'
|
|
||||||
C.Ensure -> categoryName'
|
|
||||||
C.Break -> categoryName'
|
|
||||||
C.Continue -> categoryName'
|
|
||||||
C.BeginBlock -> categoryName'
|
|
||||||
C.EndBlock -> categoryName'
|
|
||||||
C.Yield | Text.null termName -> categoryName'
|
|
||||||
C.Return | Text.null termName -> categoryName'
|
|
||||||
C.Switch | Text.null termName -> categoryName'
|
|
||||||
_ -> "the" <+> squotes (toDoc termName) <+> toDoc categoryName
|
|
||||||
where
|
|
||||||
termAndCategoryName = "the" <+> toDoc termName <+> toDoc categoryName
|
|
||||||
categoryName = toCategoryName category
|
|
||||||
categoryName' = case categoryName of
|
|
||||||
name | startsWithVowel name -> "an" <+> toDoc name
|
|
||||||
| otherwise -> "a" <+> toDoc name
|
|
||||||
startsWithVowel text = getAny $ foldMap (Any . flip Text.isPrefixOf text) vowels
|
|
||||||
vowels = Text.singleton <$> ("aeiouAEIOU" :: [Char])
|
|
||||||
|
|
||||||
-- Returns a text representing a specific term given a source and a term.
|
|
||||||
toTermName :: forall leaf fields. (StringConv leaf Text, HasDefaultFields fields) => Source -> SyntaxTerm leaf fields -> Text
|
|
||||||
toTermName source term = case unwrap term of
|
|
||||||
S.Send _ _ -> termNameFromSource term
|
|
||||||
S.Ty _ -> termNameFromSource term
|
|
||||||
S.TypeDecl id _ -> toTermName' id
|
|
||||||
S.TypeAssertion _ _ -> termNameFromSource term
|
|
||||||
S.TypeConversion _ _ -> termNameFromSource term
|
|
||||||
S.Go expr -> toTermName' expr
|
|
||||||
S.Defer expr -> toTermName' expr
|
|
||||||
S.AnonymousFunction params _ -> "anonymous" <> paramsToArgNames params
|
|
||||||
S.Fixed children -> termNameFromChildren term children
|
|
||||||
S.Indexed children -> maybe "branch" sconcat (nonEmpty (intersperse ", " (toTermName' <$> children)))
|
|
||||||
Leaf leaf -> toS leaf
|
|
||||||
S.Assignment identifier _ -> toTermName' identifier
|
|
||||||
S.Function identifier _ _ -> toTermName' identifier
|
|
||||||
S.ParameterDecl _ _ -> termNameFromSource term
|
|
||||||
S.FunctionCall i _ args -> case unwrap i of
|
|
||||||
S.AnonymousFunction params _ ->
|
|
||||||
-- Omit a function call's arguments if it's arguments match the underlying
|
|
||||||
-- anonymous function's arguments.
|
|
||||||
if (category . extract <$> args) == (category . extract <$> params)
|
|
||||||
then toTermName' i
|
|
||||||
else "(" <> toTermName' i <> ")" <> paramsToArgNames args
|
|
||||||
_ -> toTermName' i <> paramsToArgNames args
|
|
||||||
S.MemberAccess base property -> case (unwrap base, unwrap property) of
|
|
||||||
(S.FunctionCall{}, S.FunctionCall{}) -> toTermName' base <> "()." <> toTermName' property <> "()"
|
|
||||||
(S.FunctionCall{}, _) -> toTermName' base <> "()." <> toTermName' property
|
|
||||||
(_, S.FunctionCall{}) -> toTermName' base <> "." <> toTermName' property <> "()"
|
|
||||||
(_, _) -> toTermName' base <> "." <> toTermName' property
|
|
||||||
S.MethodCall targetId methodId _ methodParams -> toTermName' targetId <> sep <> toTermName' methodId <> paramsToArgNames methodParams
|
|
||||||
where sep = case unwrap targetId of
|
|
||||||
S.FunctionCall{} -> "()."
|
|
||||||
_ -> "."
|
|
||||||
S.SubscriptAccess base element -> case (unwrap base, unwrap element) of
|
|
||||||
(S.FunctionCall{}, S.FunctionCall{}) -> toTermName' base <> "()." <> toTermName' element <> "()"
|
|
||||||
(S.FunctionCall{}, _) -> toTermName' base <> "()." <> toTermName' element
|
|
||||||
(_, S.FunctionCall{}) -> toTermName' base <> "[" <> toTermName' element <> "()" <> "]"
|
|
||||||
(S.Indexed _, _) -> case category . extract $ base of
|
|
||||||
SliceTy -> termNameFromSource base <> toTermName' element
|
|
||||||
_ -> toTermName' base <> "[" <> toTermName' element <> "]"
|
|
||||||
(_, _) -> toTermName' base <> "[" <> toTermName' element <> "]"
|
|
||||||
S.VarAssignment varId _ -> termNameFromChildren term varId
|
|
||||||
S.VarDecl _ -> termNameFromSource term
|
|
||||||
-- TODO: We should remove Case from Syntax since I don't think we should ever
|
|
||||||
-- evaluate Case as a single toTermName Text - joshvera
|
|
||||||
S.Case expr _ -> termNameFromSource expr
|
|
||||||
S.Switch exprs _ -> maybe "" toTermName' (fmap snd (unsnoc exprs))
|
|
||||||
S.Ternary expr _ -> toTermName' expr
|
|
||||||
S.OperatorAssignment id _ -> toTermName' id
|
|
||||||
S.Operator _ -> termNameFromSource term
|
|
||||||
S.Object ty kvs -> maybe ("{ " <> Text.intercalate ", " (toTermName' <$> kvs) <> " }") termNameFromSource ty
|
|
||||||
S.Pair k v -> toKeyName k <> toArgName v
|
|
||||||
S.Return children -> Text.intercalate ", " (termNameFromSource <$> children)
|
|
||||||
S.Yield children -> Text.intercalate ", " (termNameFromSource <$> children)
|
|
||||||
S.ParseError _ -> termNameFromSource term
|
|
||||||
S.If expr _ -> termNameFromSource expr
|
|
||||||
S.For clauses _ -> termNameFromChildren term clauses
|
|
||||||
S.While expr _ -> toTermName' expr
|
|
||||||
S.DoWhile _ expr -> toTermName' expr
|
|
||||||
S.Throw expr -> termNameFromSource expr
|
|
||||||
S.Constructor expr -> toTermName' expr
|
|
||||||
S.Try clauses _ _ _ -> termNameFromChildren term clauses
|
|
||||||
S.Select clauses -> termNameFromChildren term clauses
|
|
||||||
S.Array ty _ -> maybe (termNameFromSource term) termNameFromSource ty
|
|
||||||
S.Class identifier _ _ -> toTermName' identifier
|
|
||||||
S.Method _ identifier (Just receiver) args _ -> termNameFromSource receiver <> "." <> toTermName' identifier <> paramsToArgNames args
|
|
||||||
S.Method _ identifier Nothing args _ -> toTermName' identifier <> paramsToArgNames args
|
|
||||||
S.Comment a -> toS a
|
|
||||||
S.Commented _ _ -> termNameFromChildren term (toList $ unwrap term)
|
|
||||||
S.Module identifier _ -> toTermName' identifier
|
|
||||||
S.Namespace identifier _ -> toTermName' identifier
|
|
||||||
S.Interface identifier _ _ -> toTermName' identifier
|
|
||||||
S.Import identifier [] -> termNameFromSource identifier
|
|
||||||
S.Import identifier exprs -> termNameFromChildren term exprs <> " from " <> toTermName' identifier
|
|
||||||
S.Export Nothing expr -> "{ " <> Text.intercalate ", " (termNameFromSource <$> expr) <> " }"
|
|
||||||
S.Export (Just identifier) [] -> "{ " <> toTermName' identifier <> " }"
|
|
||||||
S.Export (Just identifier) expr -> "{ " <> Text.intercalate ", " (termNameFromSource <$> expr) <> " }" <> " from " <> toTermName' identifier
|
|
||||||
S.Negate expr -> toTermName' expr
|
|
||||||
S.Struct ty _ -> maybe (termNameFromSource term) termNameFromSource ty
|
|
||||||
S.Rescue args _ -> Text.intercalate ", " $ toTermName' <$> args
|
|
||||||
S.Break expr -> maybe "" toTermName' expr
|
|
||||||
S.Continue expr -> maybe "" toTermName' expr
|
|
||||||
S.BlockStatement children -> termNameFromChildren term children
|
|
||||||
S.DefaultCase children -> termNameFromChildren term children
|
|
||||||
S.FieldDecl children -> termNameFromChildren term children
|
|
||||||
where toTermName' = toTermName source
|
|
||||||
termNameFromChildren term children = termNameFromRange (unionRangesFrom (range term) (range <$> children))
|
|
||||||
termNameFromSource term = termNameFromRange (range term)
|
|
||||||
termNameFromRange range = toText $ Source.slice range source
|
|
||||||
range = byteRange . extract
|
|
||||||
paramsToArgNames params = "(" <> Text.intercalate ", " (toArgName <$> params) <> ")"
|
|
||||||
toArgName :: SyntaxTerm leaf fields -> Text
|
|
||||||
toArgName arg = case identifiable arg of
|
|
||||||
Identifiable arg -> toTermName' arg
|
|
||||||
Unidentifiable _ -> "…"
|
|
||||||
toKeyName key = case toTermName' key of
|
|
||||||
n | Text.head n == ':' -> n <> " => "
|
|
||||||
n -> n <> ": "
|
|
||||||
|
|
||||||
parentContexts :: [Either (Category, Text) (Category, Text)] -> Doc
|
|
||||||
parentContexts contexts = hsep $ either identifiableDoc annotatableDoc <$> contexts
|
|
||||||
where
|
|
||||||
identifiableDoc (c, t) = case c of
|
|
||||||
C.Assignment -> "in an" <+> catName c <+> "to" <+> termName t
|
|
||||||
C.Select -> "in a" <+> catName c
|
|
||||||
C.Begin -> "in a" <+> catName c
|
|
||||||
C.Else -> "in an" <+> catName c
|
|
||||||
C.Elsif -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Method -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.SingletonMethod -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Ternary -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Ensure -> "in an" <+> catName c
|
|
||||||
C.Rescue -> case t of
|
|
||||||
"" -> "in a" <+> catName c
|
|
||||||
_ -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Modifier C.Rescue -> "in the" <+> squotes ("rescue" <+> termName t) <+> "modifier"
|
|
||||||
C.If -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Case -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Break -> case t of
|
|
||||||
"" -> "in a" <+> catName c
|
|
||||||
_ -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Continue -> case t of
|
|
||||||
"" -> "in a" <+> catName c
|
|
||||||
_ -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.Switch -> case t of
|
|
||||||
"" -> "in a" <+> catName c
|
|
||||||
_ -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
C.When -> "in a" <+> catName c
|
|
||||||
C.BeginBlock -> "in a" <+> catName c
|
|
||||||
C.EndBlock -> "in an" <+> catName c
|
|
||||||
C.DefaultCase -> "in a" <+> catName c
|
|
||||||
C.TypeDecl -> "in the" <+> squotes (termName t) <+> catName c
|
|
||||||
_ -> "in the" <+> termName t <+> catName c
|
|
||||||
annotatableDoc (c, t) = "of the" <+> squotes (termName t) <+> catName c
|
|
||||||
catName = toDoc . toCategoryName
|
|
||||||
termName = toDoc
|
|
||||||
|
|
||||||
toDoc :: Text -> Doc
|
|
||||||
toDoc = string . toS
|
|
||||||
|
|
||||||
termToDiffInfo :: (StringConv leaf Text, HasDefaultFields fields) => Source -> SyntaxTerm leaf fields -> DiffInfo
|
|
||||||
termToDiffInfo blob term = case unwrap term of
|
|
||||||
S.Indexed children -> BranchInfo (termToDiffInfo' <$> children) (category $ extract term) BIndexed
|
|
||||||
S.Fixed children -> BranchInfo (termToDiffInfo' <$> children) (category $ extract term) BFixed
|
|
||||||
S.AnonymousFunction _ _ -> LeafInfo C.AnonymousFunction (toTermName' term) (getField $ extract term)
|
|
||||||
S.Comment _ -> HideInfo
|
|
||||||
S.Commented cs leaf -> BranchInfo (termToDiffInfo' <$> cs <> maybeToList leaf) (category $ extract term) BCommented
|
|
||||||
S.ParseError _ -> ErrorInfo (getField $ extract term) (toTermName' term)
|
|
||||||
_ -> toLeafInfo term
|
|
||||||
where toTermName' = toTermName blob
|
|
||||||
termToDiffInfo' = termToDiffInfo blob
|
|
||||||
toLeafInfo term = LeafInfo (category $ extract term) (toTermName' term) (getField $ extract term)
|
|
||||||
|
|
||||||
-- | Append a parentAnnotation to the current DiffSummary instance.
|
|
||||||
-- | For a DiffSummary without a parentAnnotation, we append a parentAnnotation with the first identifiable term.
|
|
||||||
-- | For a DiffSummary with a parentAnnotation, we append the next annotatable term to the extant parentAnnotation.
|
|
||||||
-- | If a DiffSummary already has a parentAnnotation, and a (grand) parentAnnotation, then we return the summary without modification.
|
|
||||||
appendSummary :: (StringConv leaf Text, HasDefaultFields fields) => Source -> SyntaxTerm leaf fields -> DiffSummary DiffInfo -> DiffSummary DiffInfo
|
|
||||||
appendSummary source term summary =
|
|
||||||
case (parentAnnotation summary, identifiable term, annotatable term) of
|
|
||||||
([], Identifiable _, _) -> appendParentAnnotation Left
|
|
||||||
([_], _, Annotatable _) -> appendParentAnnotation Right
|
|
||||||
(_, _, _) -> summary
|
|
||||||
where
|
|
||||||
appendParentAnnotation constructor = summary
|
|
||||||
{ parentAnnotation = parentAnnotation summary <> [ constructor (category (extract term), toTermName source term) ] }
|
|
||||||
|
|
||||||
isBranchInfo :: DiffInfo -> Bool
|
|
||||||
isBranchInfo info = case info of
|
|
||||||
BranchInfo{} -> True
|
|
||||||
_ -> False
|
|
||||||
|
|
||||||
-- The user-facing category name of 'a'.
|
|
||||||
class HasCategory a where
|
|
||||||
toCategoryName :: a -> Text
|
|
||||||
|
|
||||||
-- Instances
|
|
||||||
|
|
||||||
instance HasCategory Text where
|
|
||||||
toCategoryName = identity
|
|
||||||
|
|
||||||
instance HasCategory Category where
|
|
||||||
toCategoryName category = case category of
|
|
||||||
C.Ty -> "type"
|
|
||||||
ArrayLiteral -> "array"
|
|
||||||
BooleanOperator -> "boolean operator"
|
|
||||||
MathOperator -> "math operator"
|
|
||||||
BitwiseOperator -> "bitwise operator"
|
|
||||||
RelationalOperator -> "relational operator"
|
|
||||||
Boolean -> "boolean"
|
|
||||||
DictionaryLiteral -> "dictionary"
|
|
||||||
C.Comment -> "comment"
|
|
||||||
C.ParseError -> "error"
|
|
||||||
ExpressionStatements -> "expression statements"
|
|
||||||
C.Assignment -> "assignment"
|
|
||||||
C.Function -> "function"
|
|
||||||
C.FunctionCall -> "function call"
|
|
||||||
C.MemberAccess -> "member access"
|
|
||||||
C.MethodCall -> "method call"
|
|
||||||
C.Args -> "arguments"
|
|
||||||
C.VarAssignment -> "var assignment"
|
|
||||||
C.VarDecl -> "variable"
|
|
||||||
C.Switch -> "switch statement"
|
|
||||||
C.Case -> "case statement"
|
|
||||||
C.SubscriptAccess -> "subscript access"
|
|
||||||
C.MathAssignment -> "math assignment"
|
|
||||||
C.Ternary -> "ternary expression"
|
|
||||||
C.Operator -> "operator"
|
|
||||||
Identifier -> "identifier"
|
|
||||||
IntegerLiteral -> "integer"
|
|
||||||
NumberLiteral -> "number"
|
|
||||||
FloatLiteral -> "float"
|
|
||||||
Other s -> s
|
|
||||||
C.Pair -> "pair"
|
|
||||||
C.Params -> "params"
|
|
||||||
Program -> "top level"
|
|
||||||
Regex -> "regex"
|
|
||||||
StringLiteral -> "string"
|
|
||||||
SymbolLiteral -> "symbol"
|
|
||||||
TemplateString -> "template string"
|
|
||||||
C.For -> "for statement"
|
|
||||||
C.While -> "while statement"
|
|
||||||
C.DoWhile -> "do/while statement"
|
|
||||||
C.Object -> "object"
|
|
||||||
C.Return -> "return statement"
|
|
||||||
C.Throw -> "throw statement"
|
|
||||||
C.Constructor -> "constructor"
|
|
||||||
C.Catch -> "catch statement"
|
|
||||||
C.Try -> "try statement"
|
|
||||||
C.Finally -> "finally statement"
|
|
||||||
C.Class -> "class"
|
|
||||||
C.Method -> "method"
|
|
||||||
C.If -> "if statement"
|
|
||||||
C.CommaOperator -> "comma operator"
|
|
||||||
C.Empty -> "empty statement"
|
|
||||||
C.Module -> "module"
|
|
||||||
C.Namespace -> "namespace"
|
|
||||||
C.Interface -> "interface"
|
|
||||||
C.Import -> "import statement"
|
|
||||||
C.Export -> "export statement"
|
|
||||||
C.AnonymousFunction -> "anonymous function"
|
|
||||||
C.Interpolation -> "interpolation"
|
|
||||||
C.Subshell -> "subshell command"
|
|
||||||
C.OperatorAssignment -> "operator assignment"
|
|
||||||
C.Yield -> "yield statement"
|
|
||||||
C.Until -> "until statement"
|
|
||||||
C.Unless -> "unless statement"
|
|
||||||
C.Begin -> "begin statement"
|
|
||||||
C.Else -> "else block"
|
|
||||||
C.Elsif -> "elsif block"
|
|
||||||
C.Ensure -> "ensure block"
|
|
||||||
C.Rescue -> "rescue block"
|
|
||||||
C.RescueModifier -> "rescue modifier"
|
|
||||||
C.When -> "when comparison"
|
|
||||||
C.RescuedException -> "last exception"
|
|
||||||
C.RescueArgs -> "arguments"
|
|
||||||
C.Negate -> "negate"
|
|
||||||
C.Select -> "select statement"
|
|
||||||
C.Go -> "go statement"
|
|
||||||
C.Slice -> "slice literal"
|
|
||||||
C.Defer -> "defer statement"
|
|
||||||
C.TypeAssertion -> "type assertion statement"
|
|
||||||
C.TypeConversion -> "type conversion expression"
|
|
||||||
C.ArgumentPair -> "argument"
|
|
||||||
C.KeywordParameter -> "parameter"
|
|
||||||
C.OptionalParameter -> "parameter"
|
|
||||||
C.SplatParameter -> "parameter"
|
|
||||||
C.HashSplatParameter -> "parameter"
|
|
||||||
C.BlockParameter -> "parameter"
|
|
||||||
C.ArrayTy -> "array type"
|
|
||||||
C.DictionaryTy -> "dictionary type"
|
|
||||||
C.StructTy -> "struct type"
|
|
||||||
C.Struct -> "struct"
|
|
||||||
C.Break -> "break statement"
|
|
||||||
C.Continue -> "continue statement"
|
|
||||||
C.Binary -> "binary statement"
|
|
||||||
C.Unary -> "unary statement"
|
|
||||||
C.Constant -> "constant"
|
|
||||||
C.Superclass -> "superclass"
|
|
||||||
C.SingletonClass -> "singleton class"
|
|
||||||
C.SingletonMethod -> "method"
|
|
||||||
C.RangeExpression -> "range"
|
|
||||||
C.ScopeOperator -> "scope operator"
|
|
||||||
C.BeginBlock -> "BEGIN block"
|
|
||||||
C.EndBlock -> "END block"
|
|
||||||
C.ParameterDecl -> "parameter declaration"
|
|
||||||
C.DefaultCase -> "default statement"
|
|
||||||
C.TypeDecl -> "type declaration"
|
|
||||||
C.PointerTy -> "pointer type"
|
|
||||||
C.FieldDecl -> "field declaration"
|
|
||||||
C.SliceTy -> "slice type"
|
|
||||||
C.Element -> "element"
|
|
||||||
C.Literal -> "literal"
|
|
||||||
C.ChannelTy -> "channel type"
|
|
||||||
C.Send -> "send statement"
|
|
||||||
C.IndexExpression -> "index expression"
|
|
||||||
C.FunctionTy -> "function type"
|
|
||||||
C.IncrementStatement -> "increment statement"
|
|
||||||
C.DecrementStatement -> "decrement statement"
|
|
||||||
C.QualifiedIdentifier -> "qualified identifier"
|
|
||||||
C.FieldDeclarations -> "field declarations"
|
|
||||||
C.RuneLiteral -> "rune literal"
|
|
||||||
C.Modifier C.Rescue -> "rescue modifier"
|
|
||||||
C.Modifier c -> toCategoryName c
|
|
||||||
|
|
||||||
instance HasField fields Category => HasCategory (SyntaxTerm leaf fields) where
|
|
||||||
toCategoryName = toCategoryName . category . extract
|
|
||||||
|
|
||||||
instance Listable Branch where
|
|
||||||
tiers = cons0 BIndexed \/ cons0 BFixed \/ cons0 BCommented \/ cons0 BIf
|
|
||||||
|
|
||||||
instance Listable1 DiffSummary where
|
|
||||||
liftTiers termTiers = liftCons2 (liftTiers termTiers) (liftTiers (eitherTiers (liftTiers (mapT unListableText tiers)))) DiffSummary
|
|
||||||
where eitherTiers tiers = liftTiers2 tiers tiers
|
|
||||||
|
|
||||||
instance Listable a => Listable (DiffSummary a) where
|
|
||||||
tiers = tiers1
|
|
||||||
|
|
||||||
instance P.Pretty DiffInfo where
|
|
||||||
pretty LeafInfo{..} = squotes (string $ toSL termName) <+> string (toSL (toCategoryName leafCategory))
|
|
||||||
pretty BranchInfo{..} = mconcat $ punctuate (string "," P.<> space) (pretty <$> branches)
|
|
||||||
pretty ErrorInfo{..} = squotes (string $ toSL termName) <+> "at" <+> (string . toSL $ displayStartEndPos errorSpan)
|
|
||||||
pretty HideInfo = ""
|
|
@ -1,7 +1,8 @@
|
|||||||
{-# LANGUAGE DeriveAnyClass, RankNTypes #-}
|
{-# LANGUAGE DeriveAnyClass, MultiParamTypeClasses, RankNTypes #-}
|
||||||
module Renderer.TOC
|
module Renderer.TOC
|
||||||
( toc
|
( toc
|
||||||
, diffTOC
|
, diffTOC
|
||||||
|
, Summaries(..)
|
||||||
, JSONSummary(..)
|
, JSONSummary(..)
|
||||||
, Summarizable(..)
|
, Summarizable(..)
|
||||||
, isValidSummary
|
, isValidSummary
|
||||||
@ -28,13 +29,25 @@ import Diff
|
|||||||
import Info
|
import Info
|
||||||
import Patch
|
import Patch
|
||||||
import Prologue
|
import Prologue
|
||||||
import Renderer.Summary (Summaries(..))
|
|
||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
import qualified Data.Map as Map hiding (null)
|
import qualified Data.Map as Map hiding (null)
|
||||||
import Source hiding (null)
|
import Source hiding (null)
|
||||||
import Syntax as S
|
import Syntax as S
|
||||||
import Term
|
import Term
|
||||||
|
|
||||||
|
data Summaries = Summaries { changes, errors :: !(Map Text [Value]) }
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
instance Monoid Summaries where
|
||||||
|
mempty = Summaries mempty mempty
|
||||||
|
mappend (Summaries c1 e1) (Summaries c2 e2) = Summaries (Map.unionWith (<>) c1 c2) (Map.unionWith (<>) e1 e2)
|
||||||
|
|
||||||
|
instance StringConv Summaries ByteString where
|
||||||
|
strConv _ = toS . (<> "\n") . encode
|
||||||
|
|
||||||
|
instance ToJSON Summaries where
|
||||||
|
toJSON Summaries{..} = object [ "changes" .= changes, "errors" .= errors ]
|
||||||
|
|
||||||
data JSONSummary = JSONSummary { info :: Summarizable }
|
data JSONSummary = JSONSummary { info :: Summarizable }
|
||||||
| ErrorSummary { error :: Text, errorSpan :: SourceSpan }
|
| ErrorSummary { error :: Text, errorSpan :: SourceSpan }
|
||||||
deriving (Generic, Eq, Show)
|
deriving (Generic, Eq, Show)
|
||||||
|
@ -76,7 +76,6 @@ arguments gitDir alternates = info (version <*> helper <*> argumentsParser) desc
|
|||||||
diffArgumentsParser = Diff
|
diffArgumentsParser = Diff
|
||||||
<$> ( ( flag patchDiff patchDiff (long "patch" <> help "Output a patch(1)-compatible diff (default)")
|
<$> ( ( flag patchDiff patchDiff (long "patch" <> help "Output a patch(1)-compatible diff (default)")
|
||||||
<|> flag' jsonDiff (long "json" <> help "Output a json diff")
|
<|> flag' jsonDiff (long "json" <> help "Output a json diff")
|
||||||
<|> flag' summaryDiff (long "summary" <> help "Output a diff summary")
|
|
||||||
<|> flag' sExpressionDiff (long "sexpression" <> help "Output an s-expression diff tree")
|
<|> flag' sExpressionDiff (long "sexpression" <> help "Output an s-expression diff tree")
|
||||||
<|> flag' tocDiff (long "toc" <> help "Output a table of contents diff summary") )
|
<|> flag' tocDiff (long "toc" <> help "Output a table of contents diff summary") )
|
||||||
<*> ( DiffPaths
|
<*> ( DiffPaths
|
||||||
|
@ -55,11 +55,6 @@ spec = parallel $ do
|
|||||||
blobs `shouldBe` [both b b]
|
blobs `shouldBe` [both b b]
|
||||||
|
|
||||||
describe "fetchDiffs" $ do
|
describe "fetchDiffs" $ do
|
||||||
it "generates diff summaries for two shas" $ do
|
|
||||||
(errors, summaries) <- fetchDiffsOutput summaryText "test/fixtures/git/examples/all-languages.git" "dfac8fd681b0749af137aebf3203e77a06fbafc2" "2e4144eb8c44f007463ec34cb66353f0041161fe" [("methods.rb", Just Ruby)] (const identity) Renderer.SummaryRenderer
|
|
||||||
errors `shouldBe` Just (fromList [])
|
|
||||||
summaries `shouldBe` Just (fromList [("methods.rb", ["Added the 'foo()' method"])])
|
|
||||||
|
|
||||||
it "generates toc summaries for two shas" $ do
|
it "generates toc summaries for two shas" $ do
|
||||||
(errors, summaries) <- fetchDiffsOutput termText "test/fixtures/git/examples/all-languages.git" "dfac8fd681b0749af137aebf3203e77a06fbafc2" "2e4144eb8c44f007463ec34cb66353f0041161fe" [("methods.rb", Just Ruby)] declarationDecorator Renderer.ToCRenderer
|
(errors, summaries) <- fetchDiffsOutput termText "test/fixtures/git/examples/all-languages.git" "dfac8fd681b0749af137aebf3203e77a06fbafc2" "2e4144eb8c44f007463ec34cb66353f0041161fe" [("methods.rb", Just Ruby)] declarationDecorator Renderer.ToCRenderer
|
||||||
errors `shouldBe` Just (fromList [])
|
errors `shouldBe` Just (fromList [])
|
||||||
@ -71,11 +66,11 @@ spec = parallel $ do
|
|||||||
summaries `shouldBe` Just (fromList [("methods.rb", ["foo"])])
|
summaries `shouldBe` Just (fromList [("methods.rb", ["foo"])])
|
||||||
|
|
||||||
it "errors with bad shas" $
|
it "errors with bad shas" $
|
||||||
fetchDiffsOutput summaryText "test/fixtures/git/examples/all-languages.git" "dead" "beef" [("methods.rb", Just Ruby)] (const identity) Renderer.SummaryRenderer
|
fetchDiffsOutput summaryText "test/fixtures/git/examples/all-languages.git" "dead" "beef" [("methods.rb", Just Ruby)] declarationDecorator Renderer.ToCRenderer
|
||||||
`shouldThrow` (== Git.BackendError "Could not lookup dead: Object not found - no match for prefix (dead000000000000000000000000000000000000)")
|
`shouldThrow` (== Git.BackendError "Could not lookup dead: Object not found - no match for prefix (dead000000000000000000000000000000000000)")
|
||||||
|
|
||||||
it "errors with bad repo path" $
|
it "errors with bad repo path" $
|
||||||
fetchDiffsOutput summaryText "test/fixtures/git/examples/not-a-repo.git" "dfac8fd681b0749af137aebf3203e77a06fbafc2" "2e4144eb8c44f007463ec34cb66353f0041161fe" [("methods.rb", Just Ruby)] (const identity) Renderer.SummaryRenderer
|
fetchDiffsOutput summaryText "test/fixtures/git/examples/not-a-repo.git" "dfac8fd681b0749af137aebf3203e77a06fbafc2" "2e4144eb8c44f007463ec34cb66353f0041161fe" [("methods.rb", Just Ruby)] declarationDecorator Renderer.ToCRenderer
|
||||||
`shouldThrow` errorCall "Could not open repository \"test/fixtures/git/examples/not-a-repo.git\""
|
`shouldThrow` errorCall "Could not open repository \"test/fixtures/git/examples/not-a-repo.git\""
|
||||||
|
|
||||||
where repoPath = "test/fixtures/git/examples/all-languages.git"
|
where repoPath = "test/fixtures/git/examples/all-languages.git"
|
||||||
|
@ -62,12 +62,10 @@ data DiffFixture = DiffFixture
|
|||||||
instance Listable DiffFixture where
|
instance Listable DiffFixture where
|
||||||
tiers = cons0 (DiffFixture (patchDiff pathMode "" []) patchOutput)
|
tiers = cons0 (DiffFixture (patchDiff pathMode "" []) patchOutput)
|
||||||
\/ cons0 (DiffFixture (jsonDiff pathMode "" []) jsonOutput)
|
\/ cons0 (DiffFixture (jsonDiff pathMode "" []) jsonOutput)
|
||||||
\/ cons0 (DiffFixture (summaryDiff pathMode "" []) summaryOutput)
|
|
||||||
\/ cons0 (DiffFixture (sExpressionDiff pathMode "" []) sExpressionOutput)
|
\/ cons0 (DiffFixture (sExpressionDiff pathMode "" []) sExpressionOutput)
|
||||||
\/ cons0 (DiffFixture (tocDiff pathMode "" []) tocOutput)
|
\/ cons0 (DiffFixture (tocDiff pathMode "" []) tocOutput)
|
||||||
\/ cons0 (DiffFixture (patchDiff commitMode repo []) patchOutput')
|
\/ cons0 (DiffFixture (patchDiff commitMode repo []) patchOutput')
|
||||||
\/ cons0 (DiffFixture (jsonDiff commitMode repo []) jsonOutput')
|
\/ cons0 (DiffFixture (jsonDiff commitMode repo []) jsonOutput')
|
||||||
\/ cons0 (DiffFixture (summaryDiff commitMode repo []) summaryOutput')
|
|
||||||
\/ cons0 (DiffFixture (sExpressionDiff commitMode repo []) sExpressionOutput')
|
\/ cons0 (DiffFixture (sExpressionDiff commitMode repo []) sExpressionOutput')
|
||||||
\/ cons0 (DiffFixture (tocDiff commitMode repo []) tocOutput')
|
\/ cons0 (DiffFixture (tocDiff commitMode repo []) tocOutput')
|
||||||
|
|
||||||
@ -77,8 +75,6 @@ instance Listable DiffFixture where
|
|||||||
|
|
||||||
patchOutput = "diff --git a/test/fixtures/ruby/method-declaration.A.rb b/test/fixtures/ruby/method-declaration.B.rb\nindex 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644\n--- a/test/fixtures/ruby/method-declaration.A.rb\n+++ b/test/fixtures/ruby/method-declaration.B.rb\n@@ -1,3 +1,4 @@\n-def foo\n+def bar(a)\n+ baz\n end\n\n"
|
patchOutput = "diff --git a/test/fixtures/ruby/method-declaration.A.rb b/test/fixtures/ruby/method-declaration.B.rb\nindex 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644\n--- a/test/fixtures/ruby/method-declaration.A.rb\n+++ b/test/fixtures/ruby/method-declaration.B.rb\n@@ -1,3 +1,4 @@\n-def foo\n+def bar(a)\n+ baz\n end\n\n"
|
||||||
patchOutput' = "diff --git a/methods.rb b/methods.rb\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..ff7bbbe9495f61d9e1e58c597502d152bab1761e\n--- /dev/null\n+++ b/methods.rb\n+def foo\n+end\n\n"
|
patchOutput' = "diff --git a/methods.rb b/methods.rb\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..ff7bbbe9495f61d9e1e58c597502d152bab1761e\n--- /dev/null\n+++ b/methods.rb\n+def foo\n+end\n\n"
|
||||||
summaryOutput = "{\"changes\":{\"test/fixtures/ruby/method-declaration.A.rb -> test/fixtures/ruby/method-declaration.B.rb\":[{\"span\":{\"replace\":[{\"start\":[1,5],\"end\":[1,8]},{\"start\":[1,5],\"end\":[1,8]}]},\"summary\":\"Replaced the 'foo' identifier with the 'bar' identifier in the 'bar(\226\128\166)' method\"},{\"span\":{\"insert\":{\"start\":[1,9],\"end\":[1,10]}},\"summary\":\"Added the 'a' identifier in the 'bar(\226\128\166)' method\"},{\"span\":{\"insert\":{\"start\":[2,3],\"end\":[2,6]}},\"summary\":\"Added the 'baz' identifier in the 'bar(\226\128\166)' method\"}]},\"errors\":{}}\n"
|
|
||||||
summaryOutput' = "{\"changes\":{\"methods.rb\":[{\"span\":{\"insert\":{\"start\":[1,1],\"end\":[2,4]}},\"summary\":\"Added the 'foo()' method\"}]},\"errors\":{}}\n"
|
|
||||||
|
|
||||||
jsonOutput = "{\"oids\":[\"0000000000000000000000000000000000000000\",\"0000000000000000000000000000000000000000\"],\"paths\":[\"test/fixtures/ruby/method-declaration.A.rb\",\"test/fixtures/ruby/method-declaration.B.rb\"],\"rows\":[[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"replace\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[0,8],\"number\":1,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"replace\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}},{\"insert\":{\"category\":\"Params\",\"children\":[{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[8,9],\"sourceSpan\":{\"start\":[1,9],\"end\":[1,10]}}],\"sourceRange\":[7,11],\"sourceSpan\":{\"start\":[1,8],\"end\":[2,3]}}}],\"sourceRange\":[0,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[0,11],\"number\":1,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"insert\":{\"category\":\"Params\",\"children\":[],\"sourceRange\":[11,13],\"sourceSpan\":{\"start\":[1,8],\"end\":[2,3]}}},{\"insert\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[13,16],\"sourceSpan\":{\"start\":[2,3],\"end\":[2,6]}}}],\"sourceRange\":[11,17],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[11,17],\"number\":2,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[8,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[8,12],\"number\":2,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[17,20],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[17,21],\"number\":3,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[],\"sourceRange\":[12,12],\"number\":3,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[],\"sourceRange\":[21,21],\"number\":4,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}]]}\n"
|
jsonOutput = "{\"oids\":[\"0000000000000000000000000000000000000000\",\"0000000000000000000000000000000000000000\"],\"paths\":[\"test/fixtures/ruby/method-declaration.A.rb\",\"test/fixtures/ruby/method-declaration.B.rb\"],\"rows\":[[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"replace\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[0,8],\"number\":1,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"replace\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}},{\"insert\":{\"category\":\"Params\",\"children\":[{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[8,9],\"sourceSpan\":{\"start\":[1,9],\"end\":[1,10]}}],\"sourceRange\":[7,11],\"sourceSpan\":{\"start\":[1,8],\"end\":[2,3]}}}],\"sourceRange\":[0,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[0,11],\"number\":1,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"insert\":{\"category\":\"Params\",\"children\":[],\"sourceRange\":[11,13],\"sourceSpan\":{\"start\":[1,8],\"end\":[2,3]}}},{\"insert\":{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[13,16],\"sourceSpan\":{\"start\":[2,3],\"end\":[2,6]}}}],\"sourceRange\":[11,17],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[11,17],\"number\":2,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[8,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[8,12],\"number\":2,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[17,20],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,4]}}],\"sourceRange\":[17,21],\"number\":3,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}],[{\"category\":\"Program\",\"children\":[],\"sourceRange\":[12,12],\"number\":3,\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},{\"category\":\"Program\",\"children\":[],\"sourceRange\":[21,21],\"number\":4,\"sourceSpan\":{\"start\":[1,1],\"end\":[4,1]}}]]}\n"
|
||||||
jsonOutput' = "{\"oids\":[\"0000000000000000000000000000000000000000\",\"ff7bbbe9495f61d9e1e58c597502d152bab1761e\"],\"paths\":[\"methods.rb\",\"methods.rb\"],\"rows\":[[{\"insert\":{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":1}],[{\"insert\":{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[8,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[8,12],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":2}],[{\"insert\":{\"category\":\"Program\",\"children\":[],\"sourceRange\":[12,12],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":3}]]}\n"
|
jsonOutput' = "{\"oids\":[\"0000000000000000000000000000000000000000\",\"ff7bbbe9495f61d9e1e58c597502d152bab1761e\"],\"paths\":[\"methods.rb\",\"methods.rb\"],\"rows\":[[{\"insert\":{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[{\"category\":\"Identifier\",\"children\":[],\"sourceRange\":[4,7],\"sourceSpan\":{\"start\":[1,5],\"end\":[1,8]}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[0,8],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":1}],[{\"insert\":{\"category\":\"Program\",\"children\":[{\"category\":\"Method\",\"children\":[],\"sourceRange\":[8,11],\"sourceSpan\":{\"start\":[1,1],\"end\":[2,4]}}],\"sourceRange\":[8,12],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":2}],[{\"insert\":{\"category\":\"Program\",\"children\":[],\"sourceRange\":[12,12],\"sourceSpan\":{\"start\":[1,1],\"end\":[3,1]}},\"number\":3}]]}\n"
|
||||||
|
@ -7,7 +7,6 @@ import qualified Data.Mergeable.Spec
|
|||||||
import qualified Data.RandomWalkSimilarity.Spec
|
import qualified Data.RandomWalkSimilarity.Spec
|
||||||
import qualified Data.Syntax.Assignment.Spec
|
import qualified Data.Syntax.Assignment.Spec
|
||||||
import qualified DiffSpec
|
import qualified DiffSpec
|
||||||
import qualified SummarySpec
|
|
||||||
import qualified GitmonClientSpec
|
import qualified GitmonClientSpec
|
||||||
import qualified InterpreterSpec
|
import qualified InterpreterSpec
|
||||||
import qualified PatchOutputSpec
|
import qualified PatchOutputSpec
|
||||||
@ -30,7 +29,6 @@ main = hspec $ do
|
|||||||
describe "Data.RandomWalkSimilarity" Data.RandomWalkSimilarity.Spec.spec
|
describe "Data.RandomWalkSimilarity" Data.RandomWalkSimilarity.Spec.spec
|
||||||
describe "Data.Syntax.Assignment" Data.Syntax.Assignment.Spec.spec
|
describe "Data.Syntax.Assignment" Data.Syntax.Assignment.Spec.spec
|
||||||
describe "Diff" DiffSpec.spec
|
describe "Diff" DiffSpec.spec
|
||||||
describe "Summary" SummarySpec.spec
|
|
||||||
describe "Interpreter" InterpreterSpec.spec
|
describe "Interpreter" InterpreterSpec.spec
|
||||||
describe "PatchOutput" PatchOutputSpec.spec
|
describe "PatchOutput" PatchOutputSpec.spec
|
||||||
describe "Range" RangeSpec.spec
|
describe "Range" RangeSpec.spec
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
module SummarySpec where
|
|
||||||
|
|
||||||
import Category
|
|
||||||
import Data.Functor.Both
|
|
||||||
import Data.Functor.Listable
|
|
||||||
import Data.List (partition)
|
|
||||||
import RWS
|
|
||||||
import Data.Record
|
|
||||||
import Data.String
|
|
||||||
import Diff
|
|
||||||
import Language
|
|
||||||
import Renderer.Summary
|
|
||||||
import Info
|
|
||||||
import Interpreter
|
|
||||||
import Patch
|
|
||||||
import Prologue
|
|
||||||
import Source
|
|
||||||
import SpecHelpers
|
|
||||||
import Syntax
|
|
||||||
import Term
|
|
||||||
import Test.Hspec (Spec, describe, it, parallel)
|
|
||||||
import Test.Hspec.Expectations.Pretty
|
|
||||||
import Test.Hspec.LeanCheck
|
|
||||||
import Data.These
|
|
||||||
|
|
||||||
sourceSpanBetween :: (Int, Int) -> (Int, Int) -> SourceSpan
|
|
||||||
sourceSpanBetween (s1, e1) (s2, e2) = SourceSpan (SourcePos s1 e1) (SourcePos s2 e2)
|
|
||||||
|
|
||||||
arrayInfo :: Record '[Category, Range, SourceSpan]
|
|
||||||
arrayInfo = ArrayLiteral :. Range 0 3 :. sourceSpanBetween (1, 1) (1, 5) :. Nil
|
|
||||||
|
|
||||||
literalInfo :: Record '[Category, Range, SourceSpan]
|
|
||||||
literalInfo = StringLiteral :. Range 1 2 :. sourceSpanBetween (1, 2) (1, 4) :. Nil
|
|
||||||
|
|
||||||
testDiff :: Diff (Syntax Text) (Record '[Category, Range, SourceSpan])
|
|
||||||
testDiff = free $ Free (pure arrayInfo :< Indexed [ free $ Pure (Insert (cofree $ literalInfo :< Leaf "\"a\"")) ])
|
|
||||||
|
|
||||||
testSummary :: DiffSummary DiffInfo
|
|
||||||
testSummary = DiffSummary { diffSummaryPatch = Insert (LeafInfo StringLiteral "a" $ sourceSpanBetween (1,1) (1, 2)), parentAnnotation = [] }
|
|
||||||
|
|
||||||
replacementSummary :: DiffSummary DiffInfo
|
|
||||||
replacementSummary = DiffSummary { diffSummaryPatch = Replace (LeafInfo StringLiteral "a" $ sourceSpanBetween (1, 2) (1, 4)) (LeafInfo SymbolLiteral "b" $ sourceSpanBetween (1,1) (1, 2)), parentAnnotation = [Left (Info.FunctionCall, "foo")] }
|
|
||||||
|
|
||||||
blobs :: Both SourceBlob
|
|
||||||
blobs = both (SourceBlob (fromText "[]") nullOid "a.js" (Just defaultPlainBlob) (Just JavaScript)) (SourceBlob (fromText "[a]") nullOid "b.js" (Just defaultPlainBlob) (Just JavaScript))
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = parallel $ do
|
|
||||||
describe "diffSummaries" $ do
|
|
||||||
it "outputs a diff summary" $
|
|
||||||
diffSummaries blobs testDiff `shouldBe` [ JSONSummary "Added the \"a\" string" (SourceSpans . That $ sourceSpanBetween (1, 2) (1, 4)) ]
|
|
||||||
|
|
||||||
prop "equal terms produce identity diffs" $
|
|
||||||
\ a -> let term = defaultFeatureVectorDecorator (category . headF) (unListableF a :: SyntaxTerm String '[Category, Range, SourceSpan]) in
|
|
||||||
diffSummaries blobs (diffTerms term term) `shouldBe` []
|
|
||||||
|
|
||||||
describe "DiffInfo" $ do
|
|
||||||
prop "patches in summaries match the patches in diffs" $
|
|
||||||
\a -> let
|
|
||||||
diff = unListableDiff a :: SyntaxDiff String '[Category, Range, SourceSpan]
|
|
||||||
summaries = diffToDiffSummaries (source <$> blobs) diff
|
|
||||||
patches = toList diff
|
|
||||||
in
|
|
||||||
case (partition isBranchNode (diffSummaryPatch <$> summaries), partition isIndexedOrFixed patches) of
|
|
||||||
((branchPatches, otherPatches), (branchDiffPatches, otherDiffPatches)) ->
|
|
||||||
(() <$ branchPatches, () <$ otherPatches) `shouldBe` (() <$ branchDiffPatches, () <$ otherDiffPatches)
|
|
||||||
prop "generates one LeafInfo for each child in an arbitrary branch patch" $
|
|
||||||
\a -> let
|
|
||||||
diff = unListableDiff a :: SyntaxDiff String '[Category, Range, SourceSpan]
|
|
||||||
diffInfoPatches = diffSummaryPatch <$> diffToDiffSummaries (source <$> blobs) diff
|
|
||||||
syntaxPatches = toList diff
|
|
||||||
extractLeaves :: DiffInfo -> [DiffInfo]
|
|
||||||
extractLeaves (BranchInfo children _ _) = join $ extractLeaves <$> children
|
|
||||||
extractLeaves leaf = [ leaf ]
|
|
||||||
|
|
||||||
extractDiffLeaves :: SyntaxTerm String '[Category, Range, SourceSpan] -> [ SyntaxTerm String '[Category, Range, SourceSpan] ]
|
|
||||||
extractDiffLeaves term = case unwrap term of
|
|
||||||
(Indexed children) -> join $ extractDiffLeaves <$> children
|
|
||||||
(Fixed children) -> join $ extractDiffLeaves <$> children
|
|
||||||
Commented children leaf -> children <> maybeToList leaf >>= extractDiffLeaves
|
|
||||||
_ -> [ term ]
|
|
||||||
in
|
|
||||||
case (partition isBranchNode diffInfoPatches, partition isIndexedOrFixed syntaxPatches) of
|
|
||||||
((branchPatches, _), (diffPatches, _)) ->
|
|
||||||
let listOfLeaves = foldMap extractLeaves (join $ toList <$> branchPatches)
|
|
||||||
listOfDiffLeaves = foldMap extractDiffLeaves (diffPatches >>= toList)
|
|
||||||
in
|
|
||||||
Prologue.length listOfLeaves `shouldBe` Prologue.length listOfDiffLeaves
|
|
||||||
|
|
||||||
isIndexedOrFixed :: Patch (Term (Syntax a) annotation) -> Bool
|
|
||||||
isIndexedOrFixed = any (isIndexedOrFixed' . unwrap)
|
|
||||||
|
|
||||||
isIndexedOrFixed' :: Syntax a f -> Bool
|
|
||||||
isIndexedOrFixed' syntax = case syntax of
|
|
||||||
(Indexed _) -> True
|
|
||||||
(Fixed _) -> True
|
|
||||||
(Commented _ _) -> True
|
|
||||||
_ -> False
|
|
||||||
|
|
||||||
isBranchNode :: Patch DiffInfo -> Bool
|
|
||||||
isBranchNode = any isBranchInfo
|
|
Loading…
Reference in New Issue
Block a user