@@ -1,505 +1,102 @@
1{-# OPTIONS_GHC -Wall #-}
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Render.Type
3module Reporting.Render.Type
4− ( Localizer
4+ ( Context(..)
6− , annotation
6+ , apply
8− , diffToDocs
8+ , record
9− , Style(..)
9+ , recordSnippet
13−import Control.Arrow ((***), first)
14−import qualified Data.Map as Map
15−import qualified Data.Set as Set
16−import Data.Text (Text)
18−import qualified AST.Helpers as Help
19−import qualified AST.Type as Type
20−import qualified AST.Variable as Var
21−import qualified Reporting.Helpers as Help
14+import qualified Reporting.Helpers as H
22−import Reporting.Helpers ( Doc, (<+>), cat, dullyellow, hang, hsep, parens, sep, text, vcat )
15+import Reporting.Helpers ( Doc, (<+>) )
26−-- PUBLIC API FOR CREATING DOCS
29−toDoc :: Localizer -> Type.Canonical -> Doc
30−toDoc localizer tipe =
31− docType localizer None tipe
34−diffToDocs :: Localizer -> Type.Canonical -> Type.Canonical -> (Doc,Doc)
35−diffToDocs localizer leftType rightType =
36− case diff localizer None leftType rightType of
40− Diff leftDoc rightDoc ->
44−decl :: Localizer -> Text -> [Text] -> [(Text, [Type.Canonical])] -> Doc
45−decl localizer name vars tags =
48− hang 2 (sep (text tag : map (docType localizer App) args))
51− (hsep ("type" : map text (name : vars)))
53− zipWith (<+>) ("=" : repeat "|") (map docTag tags)
56−annotation :: Localizer -> Text -> Type.Canonical -> Doc
57−annotation localizer name tipe =
60− if Help.isOp name then parens (text name) else text name
62− case Type.collectLambdas tipe of
68− (map (docType localizer Func) parts)
73− , docType localizer None tipe
82− Map.Map Var.Canonical Text
85−varToDoc :: Localizer -> Var.Canonical -> Doc
86−varToDoc localizer var =
91− if name == Help.zeroTuple then
95− text (maybe name id (Map.lookup var localizer))
107−instance Functor Diff where
111− Diff (func left) (func right)
117−instance Applicative Diff where
121− (<*>) function argument =
122− case (function, argument) of
123− (Diff leftFunc rightFunc, Diff leftArg rightArg) ->
124− Diff (leftFunc leftArg) (rightFunc rightArg)
126− (Diff leftFunc rightFunc, Same arg) ->
127− Diff (leftFunc arg) (rightFunc arg)
129− (Same func, Diff leftArg rightArg) ->
130− Diff (func leftArg) (func rightArg)
132− (Same func, Same arg) ->
136−partitionDiffs :: Map.Map k (Diff a) -> ( [(k, (a,a))], [(k, a)] )
137−partitionDiffs dict =
139− collect key value (diffKeys, sameKeys) =
142− ( (key, (left, right)) : diffKeys
148− , (key, both) : sameKeys
151− Map.foldrWithKey collect ([],[]) dict
155−-- DOC DIFF
19+-- TO DOC
158data Context
22data Context
164−diff :: Localizer -> Context -> Type.Canonical -> Type.Canonical -> Diff Doc
28+lambda :: Context -> Doc -> [Doc] -> Doc
165−diff localizer context leftType rightType =
29+lambda context arg args =
167− go = diff localizer
31+ lambdaDoc =
32+ H.sep (arg : map ("->" <+>) args)
169− case (leftType, rightType) of
34+ case context of
170− (Type.Aliased leftName leftArgs _, Type.Aliased rightName rightArgs _) | leftName == rightName ->
35+ None -> lambdaDoc
171− docAppHelp localizer context leftName
36+ Func -> H.cat [ "(", lambdaDoc, ")" ]
172− <$> sequenceA (zipWith (go App) (map snd leftArgs) (map snd rightArgs))
37+ App -> H.cat [ "(", lambdaDoc, ")" ]
174− (Type.Lambda _ _, _) ->
175− diffLambda localizer context leftType rightType
177− (_, Type.Lambda _ _) ->
178− diffLambda localizer context leftType rightType
180− (Type.Var x, Type.Var y) | x == y ->
183− (Type.Type leftName leftArgs, Type.Type rightName rightArgs) ->
184− if leftName /= rightName || length leftArgs /= length rightArgs then
186− (docApp localizer context leftName leftArgs)
187− (docApp localizer context rightName rightArgs)
192− if Var.isTuple leftName then None else App
194− docAppHelp localizer context leftName
195− <$> sequenceA (zipWith (go subContext) leftArgs rightArgs)
197− (Type.Record outerLeftFields outerLeftExt, Type.Record outerRightFields outerRightExt) ->
199− (leftFields, leftExt) =
200− flattenRecord outerLeftFields outerLeftExt
202− (rightFields, rightExt) =
203− flattenRecord outerRightFields outerRightExt
205− diffRecord localizer leftFields leftExt rightFields rightExt
209− (docType localizer context leftType)
210− (docType localizer context rightType)
213−difference :: Doc -> Doc -> Diff Doc
214−difference leftDoc rightDoc =
215− Diff (dullyellow leftDoc) (dullyellow rightDoc)
222−diffLambda :: Localizer -> Context -> Type.Canonical -> Type.Canonical -> Diff Doc
223−diffLambda localizer context leftType rightType =
225− leftArgs = reverse $ Type.collectLambdas leftType
226− rightArgs = reverse $ Type.collectLambdas rightType
229− map (dullyellow . docType localizer Func) types
231− extraLefts = reverse $ extraToDoc $ drop (length rightArgs) leftArgs
232− extraRights = reverse $ extraToDoc $ drop (length leftArgs) rightArgs
235− reverse <$> sequenceA (zipWith (diff localizer Func) leftArgs rightArgs)
237− docLambda context <$>
238− case (extraLefts, extraRights, alignedArgDiff) of
242− (_, _, Same docs) ->
243− Diff (extraLefts ++ docs) (extraRights ++ docs)
245− (_, _, Diff lefts rights) ->
246− Diff (extraLefts ++ lefts) (extraRights ++ rights)
252−
40+apply :: Context -> Doc -> [Doc] -> Doc
253−diffRecord :: Localizer -> Fields -> Maybe Text -> Fields -> Maybe Text -> Diff Doc
41+apply context name args =
254−diffRecord localizer leftFields leftExt rightFields rightExt =
256− (leftOnly, both, rightOnly) =
257− vennDiagram leftFields rightFields
259− if Map.null leftOnly && Map.null rightOnly then
262− Map.map (uncurry (diff localizer None)) both
264− case partitionDiffs fieldDiffs of
268− docRecord Full (map (first text) sames)
270− if leftExt == rightExt then
271− Same (mkRecord (fmap text leftExt))
275− (mkRecord (fmap (dullyellow . text) leftExt))
276− (mkRecord (fmap (dullyellow . text) rightExt))
280− (lefts, rights) = unzipDiffs diffs
281− style = if null sames then Full else Elide
284− (docRecord style lefts (fmap text leftExt))
285− (docRecord style rights (fmap text rightExt))
290− analyzeFields (Map.keys leftOnly) (Map.keys rightOnly)
293− if Map.null both then Full else Elide
296− (docRecord style lefts (fmap text leftExt))
297− (docRecord style rights (fmap text rightExt))
300−unzipDiffs :: [(Text, (Doc, Doc))] -> ( [(Doc, Doc)], [(Doc, Doc)] )
301−unzipDiffs diffPairs =
303− unzipHelp (name, (left, right)) =
304− (,) (text name, left) (text name, right)
306− unzip (map unzipHelp diffPairs)
310−-- RECORD DIFFS HELPERS
313−analyzeFields :: [Text] -> [Text] -> ( [(Doc, Doc)], [(Doc, Doc)] )
314−analyzeFields leftOnly rightOnly =
317− Help.findTypoPairs leftOnly rightOnly
320− ( func (text name), text "..." )
322− mkFieldWith counts name =
323− mkField (if Set.member name counts then dullyellow else id) name
325− case Help.vetTypos typoPairs of
326− Just (leftTypos, rightTypos) ->
327− ( map (mkFieldWith leftTypos) leftOnly
328− , map (mkFieldWith rightTypos) rightOnly
332− ( map (mkField dullyellow) leftOnly
333− , map (mkField dullyellow) rightOnly
338− Map.Map Text Type.Canonical
342− Map.Map Text (Type.Canonical,Type.Canonical)
345−vennDiagram :: Fields -> Fields -> (Fields, DoubleFields, Fields)
346−vennDiagram leftFields rightFields =
347− ( Map.difference leftFields rightFields
348− , Map.intersectionWith (,) leftFields rightFields
349− , Map.difference rightFields leftFields
354− :: [(Text, Type.Canonical)]
355− -> Maybe Type.Canonical
356− -> (Fields, Maybe Text)
357−flattenRecord fields ext =
358− first Map.fromList (flattenRecordHelp fields ext)
362− :: [(Text, Type.Canonical)]
363− -> Maybe Type.Canonical
364− -> ([(Text, Type.Canonical)], Maybe Text)
365−flattenRecordHelp fields ext =
370− Just (Type.Var x) ->
373− Just (Type.Record subFields subExt) ->
374− flattenRecordHelp (fields ++ subFields) subExt
376− Just (Type.Aliased _ args tipe) ->
377− flattenRecordHelp fields (Just (Type.dealias args tipe))
380− error "Trying to flatten ill-formed record."
387−docType :: Localizer -> Context -> Type.Canonical -> Doc
388−docType localizer context tipe =
389− case tipe of
42+ case args of
391− docLambda context (map (docType localizer Func) (Type.collectLambdas tipe))
396− Type.Type name args ->
397− docApp localizer context name args
399− Type.Record outerFields outerExt ->
402− flattenRecordHelp outerFields outerExt
405− (map (text *** docType localizer None) fields)
408− Type.Aliased name args _ ->
409− docApp localizer context name (map snd args)
413−docLambda :: Context -> [Doc] -> Doc
414−docLambda context docs =
417− error "cannot call docLambda with an empty list"
44+ name
419− arg:rest ->
46+ _:_ ->
49+ H.hang 4 (H.sep (name : args))
420− case context of
51+ case context of
52+ App -> H.cat [ "(", applyDoc, ")" ]
421− None ->
54+ None -> applyDoc
422− sep (arg : map ("->" <+>) rest)
427− , sep (arg : map ("->" <+>) rest)
431−
57+tuple :: Doc -> Doc -> Maybe Doc -> Doc
432−
58+tuple a b maybeC =
433−docApp :: Localizer -> Context -> Var.Canonical -> [Type.Canonical] -> Doc
434−docApp localizer context name args =
436− argContext =
60+ entries =
437− if Var.isTuple name then None else App
61+ case maybeC of
62+ Nothing -> [ "(" <+> a, "," <+> b ]
63+ Just c -> [ "(" <+> a, "," <+> b, "," <+> c ]
439− docAppHelp localizer context name (map (docType localizer argContext) args)
65+ H.sep [ H.cat entries, ")" ]
442−docAppHelp :: Localizer -> Context -> Var.Canonical -> [Doc] -> Doc
443−docAppHelp localizer context name arguments =
446− if Var.isTuple name then "()" else varToDoc localizer name
449− if Var.isTuple name then
451− [ cat ("(" <+> arg : map ("," <+>) args)
458− cat [ "(", hang 4 (sep (varToDoc localizer name : arg : args)), ")" ]
461− hang 4 (sep (varToDoc localizer name : arg : args))
470−docRecord :: Style -> [(Doc,Doc)] -> Maybe Doc -> Doc
69+record :: [(Doc, Doc)] -> Maybe Doc -> Doc
471−docRecord style fields maybeExt =
70+record entries maybeExt =
472− let
71+ case (map entryToDoc entries, maybeExt) of
473− docField (name, tipe) =
474− hang 4 (sep [ name <+> ":", tipe ])
485− elision ++ map docField fields
487− case (fieldDocs, maybeExt) of
488 ([], Nothing) ->
72 ([], Nothing) ->
491− (_, Nothing) ->
75+ (fields, Nothing) ->
493− [ cat (zipWith (<+>) ("{" : repeat ",") fieldDocs)
77+ [ H.cat (zipWith (<+>) ("{" : repeat ",") fields)
497− (_, Just ext) ->
81+ (fields, Just ext) ->
499− [ hang 4 $ sep $
83+ [ H.hang 4 $ H.sep $
500 [ "{" <+> ext
84 [ "{" <+> ext
501− , cat (zipWith (<+>) (text "|" : repeat ",") fieldDocs)
85+ , H.cat (zipWith (<+>) ("|" : repeat ",") fields)
91+entryToDoc :: (Doc, Doc) -> Doc
92+entryToDoc (fieldName, fieldType) =
93+ H.hang 4 (H.sep [ fieldName <+> ":", fieldType ])
96+recordSnippet :: (Doc, Doc) -> [(Doc, Doc)] -> Doc
97+recordSnippet entry entries =
99+ field = "{" <+> entryToDoc entry
100+ fields = zipWith (<+>) (repeat ",") (map entryToDoc entries ++ ["..."])
102+ H.sep [ H.cat (field:fields), "}" ]