1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Reporting.Render.Type
13import Control.Arrow ((***), first)
14import qualified Data.Map as Map
15import qualified Data.Set as Set
16import Data.Text (Text)
18import qualified AST.Helpers as Help
19import qualified AST.Type as Type
20import qualified AST.Variable as Var
21import qualified Reporting.Helpers as Help
22import Reporting.Helpers
23 ( Doc, (<+>), cat, colon, comma, dullyellow, equals, hang, hsep
24 , lbrace, lparen, parens, rbrace, rparen, sep, text, vcat
29-- PUBLIC API FOR CREATING DOCS
32toDoc :: Localizer -> Type.Canonical -> Doc
34 docType localizer None tipe
37diffToDocs :: Localizer -> Type.Canonical -> Type.Canonical -> (Doc,Doc)
38diffToDocs localizer leftType rightType =
39 case diff localizer None leftType rightType of
43 Diff leftDoc rightDoc ->
47decl :: Localizer -> Text -> [Text] -> [(Text, [Type.Canonical])] -> Doc
48decl localizer name vars tags =
51 hang 2 (sep (text tag : map (docType localizer App) args))
54 (hsep (text "type" : map text (name : vars)))
57 (equals : repeat (text "|"))
61annotation :: Localizer -> Text -> Type.Canonical -> Doc
62annotation localizer name tipe =
65 if Help.isOp name then parens (text name) else text name
67 case Type.collectLambdas tipe of
72 (colon : repeat (text "->"))
73 (map (docType localizer Func) parts)
78 , docType localizer None tipe
87 Map.Map Var.Canonical Text
90varToDoc :: Localizer -> Var.Canonical -> Doc
91varToDoc localizer var =
96 if name == Help.zeroTuple then
100 text (maybe name id (Map.lookup var localizer))
112instance Functor Diff where
116 Diff (func left) (func right)
122instance Applicative Diff where
126 (<*>) function argument =
127 case (function, argument) of
128 (Diff leftFunc rightFunc, Diff leftArg rightArg) ->
129 Diff (leftFunc leftArg) (rightFunc rightArg)
131 (Diff leftFunc rightFunc, Same arg) ->
132 Diff (leftFunc arg) (rightFunc arg)
134 (Same func, Diff leftArg rightArg) ->
135 Diff (func leftArg) (func rightArg)
137 (Same func, Same arg) ->
141partitionDiffs :: Map.Map k (Diff a) -> ( [(k, (a,a))], [(k, a)] )
144 collect key value (diffKeys, sameKeys) =
147 ( (key, (left, right)) : diffKeys
153 , (key, both) : sameKeys
156 Map.foldrWithKey collect ([],[]) dict
169diff :: Localizer -> Context -> Type.Canonical -> Type.Canonical -> Diff Doc
170diff localizer context leftType rightType =
174 case (leftType, rightType) of
175 (Type.Aliased leftName leftArgs _, Type.Aliased rightName rightArgs _) | leftName == rightName ->
176 docAppHelp localizer context leftName
177 <$> sequenceA (zipWith (go App) (map snd leftArgs) (map snd rightArgs))
179 (Type.Lambda _ _, _) ->
180 diffLambda localizer context leftType rightType
182 (_, Type.Lambda _ _) ->
183 diffLambda localizer context leftType rightType
185 (Type.Var x, Type.Var y) | x == y ->
188 (Type.Type leftName, Type.Type rightName) | leftName == rightName ->
189 pure (varToDoc localizer leftName)
191 (Type.App (Type.Type leftName) leftArgs, Type.App (Type.Type rightName) rightArgs) ->
192 if leftName /= rightName || length leftArgs /= length rightArgs then
194 (docApp localizer context leftName leftArgs)
195 (docApp localizer context rightName rightArgs)
200 if Var.isTuple leftName then None else App
202 docAppHelp localizer context leftName
203 <$> sequenceA (zipWith (go subContext) leftArgs rightArgs)
205 (Type.App _ _, Type.App _ _) ->
206 error "Type applications without concrete names should not get here."
208 (Type.Record outerLeftFields outerLeftExt, Type.Record outerRightFields outerRightExt) ->
210 (leftFields, leftExt) =
211 flattenRecord outerLeftFields outerLeftExt
213 (rightFields, rightExt) =
214 flattenRecord outerRightFields outerRightExt
216 diffRecord localizer leftFields leftExt rightFields rightExt
220 (docType localizer context leftType)
221 (docType localizer context rightType)
224difference :: Doc -> Doc -> Diff Doc
225difference leftDoc rightDoc =
226 Diff (dullyellow leftDoc) (dullyellow rightDoc)
233diffLambda :: Localizer -> Context -> Type.Canonical -> Type.Canonical -> Diff Doc
234diffLambda localizer context leftType rightType =
236 leftArgs = reverse $ Type.collectLambdas leftType
237 rightArgs = reverse $ Type.collectLambdas rightType
240 map (dullyellow . docType localizer Func) types
242 extraLefts = reverse $ extraToDoc $ drop (length rightArgs) leftArgs
243 extraRights = reverse $ extraToDoc $ drop (length leftArgs) rightArgs
246 reverse <$> sequenceA (zipWith (diff localizer Func) leftArgs rightArgs)
248 docLambda context <$>
249 case (extraLefts, extraRights, alignedArgDiff) of
254 Diff (extraLefts ++ docs) (extraRights ++ docs)
256 (_, _, Diff lefts rights) ->
257 Diff (extraLefts ++ lefts) (extraRights ++ rights)
264diffRecord :: Localizer -> Fields -> Maybe Text -> Fields -> Maybe Text -> Diff Doc
265diffRecord localizer leftFields leftExt rightFields rightExt =
267 (leftOnly, both, rightOnly) =
268 vennDiagram leftFields rightFields
270 if Map.null leftOnly && Map.null rightOnly then
273 Map.map (uncurry (diff localizer None)) both
275 case partitionDiffs fieldDiffs of
279 docRecord Full (map (first text) sames)
281 if leftExt == rightExt then
282 Same (mkRecord (fmap text leftExt))
286 (mkRecord (fmap (dullyellow . text) leftExt))
287 (mkRecord (fmap (dullyellow . text) rightExt))
291 (lefts, rights) = unzipDiffs diffs
292 style = if null sames then Full else Elide
295 (docRecord style lefts (fmap text leftExt))
296 (docRecord style rights (fmap text rightExt))
301 analyzeFields (Map.keys leftOnly) (Map.keys rightOnly)
304 if Map.null both then Full else Elide
307 (docRecord style lefts (fmap text leftExt))
308 (docRecord style rights (fmap text rightExt))
311unzipDiffs :: [(Text, (Doc, Doc))] -> ( [(Doc, Doc)], [(Doc, Doc)] )
312unzipDiffs diffPairs =
314 unzipHelp (name, (left, right)) =
315 (,) (text name, left) (text name, right)
317 unzip (map unzipHelp diffPairs)
321-- RECORD DIFFS HELPERS
324analyzeFields :: [Text] -> [Text] -> ( [(Doc, Doc)], [(Doc, Doc)] )
325analyzeFields leftOnly rightOnly =
328 Help.findTypoPairs leftOnly rightOnly
331 ( func (text name), text "..." )
333 mkFieldWith counts name =
334 mkField (if Set.member name counts then dullyellow else id) name
336 case Help.vetTypos typoPairs of
337 Just (leftTypos, rightTypos) ->
338 ( map (mkFieldWith leftTypos) leftOnly
339 , map (mkFieldWith rightTypos) rightOnly
343 ( map (mkField dullyellow) leftOnly
344 , map (mkField dullyellow) rightOnly
349 Map.Map Text Type.Canonical
353 Map.Map Text (Type.Canonical,Type.Canonical)
356vennDiagram :: Fields -> Fields -> (Fields, DoubleFields, Fields)
357vennDiagram leftFields rightFields =
358 ( Map.difference leftFields rightFields
359 , Map.intersectionWith (,) leftFields rightFields
360 , Map.difference rightFields leftFields
365 :: [(Text, Type.Canonical)]
366 -> Maybe Type.Canonical
367 -> (Fields, Maybe Text)
368flattenRecord fields ext =
369 first Map.fromList (flattenRecordHelp fields ext)
373 :: [(Text, Type.Canonical)]
374 -> Maybe Type.Canonical
375 -> ([(Text, Type.Canonical)], Maybe Text)
376flattenRecordHelp fields ext =
384 Just (Type.Record subFields subExt) ->
385 flattenRecordHelp (fields ++ subFields) subExt
387 Just (Type.Aliased _ args tipe) ->
388 flattenRecordHelp fields (Just (Type.dealias args tipe))
391 error "Trying to flatten ill-formed record."
398docType :: Localizer -> Context -> Type.Canonical -> Doc
399docType localizer context tipe =
402 docLambda context (map (docType localizer Func) (Type.collectLambdas tipe))
408 varToDoc localizer name
410 Type.App (Type.Type name) args ->
411 docApp localizer context name args
414 error "type applications should start with a type atom"
416 Type.Record outerFields outerExt ->
419 flattenRecordHelp outerFields outerExt
422 (map (text *** docType localizer None) fields)
425 Type.Aliased name args _ ->
426 docApp localizer context name (map snd args)
430docLambda :: Context -> [Doc] -> Doc
431docLambda context docs =
434 error "cannot call docLambda with an empty list"
439 sep (arg : map (text "->" <+>) rest)
444 , sep (arg : map (text "->" <+>) rest)
450docApp :: Localizer -> Context -> Var.Canonical -> [Type.Canonical] -> Doc
451docApp localizer context name args =
454 if Var.isTuple name then None else App
456 docAppHelp localizer context name (map (docType localizer argContext) args)
459docAppHelp :: Localizer -> Context -> Var.Canonical -> [Doc] -> Doc
460docAppHelp localizer context name args =
461 if Var.isTuple name then
463 [ cat (zipWith (<+>) (lparen : repeat comma) args)
467 else if null args then
468 varToDoc localizer name
475 , hang 4 (sep (varToDoc localizer name : args))
480 hang 4 (sep (varToDoc localizer name : args))
489docRecord :: Style -> [(Doc,Doc)] -> Maybe Doc -> Doc
490docRecord style fields maybeExt =
492 docField (name, tipe) =
493 hang 4 (sep [ name <+> colon, tipe ])
504 elision ++ map docField fields
506 case (fieldDocs, maybeExt) of
512 [ cat (zipWith (<+>) (lbrace : repeat comma) fieldDocs)
520 , cat (zipWith (<+>) (text "|" : repeat comma) fieldDocs)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list