1module Reporting.Render.Type
13import Control.Arrow ((***), first)
14import qualified Data.Map as Map
15import qualified Data.Set as Set
16import Text.PrettyPrint.ANSI.Leijen
17 ( Doc, (<>), (<+>), cat, colon, comma, dullyellow, equals, hang, hsep
18 , lbrace, lparen, parens, rbrace, rparen, sep, space, text, vcat
21import qualified AST.Helpers as Help
22import qualified AST.Type as Type
23import qualified AST.Variable as Var
24import Reporting.Error.Helpers as Help
28-- PUBLIC API FOR CREATING DOCS
31toDoc :: Localizer -> Type.Canonical -> Doc
33 docType localizer tipe
36diffToDocs :: Localizer -> Type.Canonical -> Type.Canonical -> (Doc,Doc)
37diffToDocs localizer leftType rightType =
38 case diff localizer leftType rightType of
42 Diff leftDoc rightDoc ->
46decl :: Localizer -> String -> [String] -> [(String, [Type.Canonical])] -> Doc
47decl localizer name vars tags =
50 hang 2 (sep (text tag : map (docType localizer) args))
53 (hsep (map text ("type" : name : vars)))
56 (equals : repeat (text "|"))
60annotation :: Localizer -> String -> Type.Canonical -> Doc
61annotation localizer name tipe =
64 if Help.isOp name then parens (text name) else text name
66 case Type.collectLambdas tipe of
71 ((colon <> space) : repeat (text "->"))
72 (map (docType localizer) parts)
77 , docType localizer tipe
89varToDoc :: Localizer -> Var.Canonical -> Doc
90varToDoc localizer var =
92 name = Var.toString var
94 if name == "_Tuple0" then
98 text (maybe name id (Map.lookup name localizer))
110instance Functor Diff where
114 Diff (func left) (func right)
120instance Applicative Diff where
126 (Diff leftFunc rightFunc, Diff leftArg rightArg) ->
127 Diff (leftFunc leftArg) (rightFunc rightArg)
129 (Diff leftFunc rightFunc, Same arg) ->
130 Diff (leftFunc arg) (rightFunc arg)
132 (Same func, Diff leftArg rightArg) ->
133 Diff (func leftArg) (func rightArg)
135 (Same func, Same arg) ->
139partitionDiffs :: Map.Map k (Diff a) -> ( [(k, (a,a))], [(k, a)] )
142 collect key value (diffKeys, sameKeys) =
145 ( (key, (left, right)) : diffKeys
151 , (key, both) : sameKeys
154 Map.foldrWithKey collect ([],[]) dict
161diff :: Localizer -> Type.Canonical -> Type.Canonical -> Diff Doc
162diff localizer leftType rightType =
166 case (leftType, rightType) of
167 (Type.Lambda _ _, Type.Lambda _ _) ->
169 leftParts = Type.collectLambdas leftType
170 rightParts = Type.collectLambdas rightType
172 if length leftParts /= length rightParts then
174 (docLambda (map (docType localizer) leftParts))
175 (docLambda (map (docType localizer) rightParts))
178 docLambda <$> sequenceA (zipWith go leftParts rightParts)
180 (Type.Var x, Type.Var y) | x == y ->
183 (Type.Type leftName, Type.Type rightName) | leftName == rightName ->
184 pure (varToDoc localizer leftName)
186 (Type.App (Type.Type leftName) leftArgs, Type.App (Type.Type rightName) rightArgs) ->
187 if leftName /= rightName || length leftArgs /= length rightArgs then
189 (docApp localizer leftName (map (docType localizer) leftArgs))
190 (docApp localizer rightName (map (docType localizer) rightArgs))
193 docApp localizer leftName
194 <$> sequenceA (zipWith go leftArgs rightArgs)
196 (Type.App _ _, Type.App _ _) ->
197 error "Type applications without concrete names should not get here."
199 (Type.Record outerLeftFields outerLeftExt, Type.Record outerRightFields outerRightExt) ->
201 (leftFields, leftExt) =
202 flattenRecord outerLeftFields outerLeftExt
204 (rightFields, rightExt) =
205 flattenRecord outerRightFields outerRightExt
207 diffRecord localizer leftFields leftExt rightFields rightExt
209 (Type.Aliased leftName leftArgs _, Type.Aliased rightName rightArgs _) | leftName == rightName ->
210 docApp localizer leftName
211 <$> sequenceA (zipWith go (map snd leftArgs) (map snd rightArgs))
213 (Type.Aliased _ args real, _) ->
214 go (Type.dealias args real) rightType
216 (_, Type.Aliased _ args real) ->
217 go leftType (Type.dealias args real)
221 (docType localizer leftType)
222 (docType localizer rightType)
225difference :: Doc -> Doc -> Diff Doc
226difference leftDoc rightDoc =
227 Diff (dullyellow leftDoc) (dullyellow rightDoc)
234diffRecord :: Localizer -> Fields -> Maybe String -> Fields -> Maybe String -> Diff Doc
235diffRecord localizer leftFields leftExt rightFields rightExt =
237 (leftOnly, both, rightOnly) =
238 vennDiagram leftFields rightFields
240 if Map.null leftOnly && Map.null rightOnly then
243 Map.map (uncurry (diff localizer)) both
245 case partitionDiffs fieldDiffs of
249 docRecord Full (map (first text) sames)
251 if leftExt == rightExt then
252 Same (mkRecord (fmap text leftExt))
256 (mkRecord (fmap (dullyellow . text) leftExt))
257 (mkRecord (fmap (dullyellow . text) rightExt))
261 (lefts, rights) = unzipDiffs diffs
262 style = if null sames then Full else Elide
265 (docRecord style lefts (fmap text leftExt))
266 (docRecord style rights (fmap text rightExt))
271 analyzeFields (Map.keys leftOnly) (Map.keys rightOnly)
274 if Map.null both then Full else Elide
277 (docRecord style lefts (fmap text leftExt))
278 (docRecord style rights (fmap text rightExt))
281unzipDiffs :: [(String, (Doc, Doc))] -> ( [(Doc, Doc)], [(Doc, Doc)] )
282unzipDiffs diffPairs =
284 unzipHelp (name, (left, right)) =
285 (,) (text name, left) (text name, right)
287 unzip (map unzipHelp diffPairs)
291-- ANALYZE FIELD NAMES FOR SIMILAR NAMES
294analyzeFields :: [String] -> [String] -> ( [(Doc, Doc)], [(Doc, Doc)] )
295analyzeFields leftOnly rightOnly =
298 findPotentialTypos leftOnly rightOnly
301 ( func (text name), text "…" )
303 mkFieldWith counts name =
304 mkField (if Set.member name counts then dullyellow else id) name
306 case vetTypos potentialTypos of
307 Just (leftTypos, rightTypos) ->
308 ( map (mkFieldWith leftTypos) leftOnly
309 , map (mkFieldWith rightTypos) rightOnly
313 ( map (mkField dullyellow) leftOnly
314 , map (mkField dullyellow) rightOnly
318findPotentialTypos :: [String] -> [String] -> [(String, String)]
319findPotentialTypos leftOnly rightOnly =
322 map ((,) leftName) (filter ((==1) . Help.distance leftName) rightOnly)
324 concatMap veryNear leftOnly
327vetTypos :: [(String, String)] -> Maybe (Set.Set String, Set.Set String)
328vetTypos potentialTypos =
330 tallyNames (ln, rn) (lc, rc) =
331 ( Map.insertWith (+) ln 1 lc
332 , Map.insertWith (+) rn 1 rc
335 (leftCounts, rightCounts) =
336 foldr tallyNames (Map.empty, Map.empty) potentialTypos
339 Map.foldr (\n unique -> n < 2 && unique) True counts
341 if allUnique leftCounts && allUnique rightCounts then
342 Just (Map.keysSet leftCounts, Map.keysSet rightCounts)
348-- RECORD DIFFS HELPERS
352 Map.Map String Type.Canonical
356 Map.Map String (Type.Canonical,Type.Canonical)
359vennDiagram :: Fields -> Fields -> (Fields, DoubleFields, Fields)
360vennDiagram leftFields rightFields =
361 ( Map.difference leftFields rightFields
362 , Map.intersectionWith (,) leftFields rightFields
363 , Map.difference rightFields leftFields
368 :: [(String, Type.Canonical)]
369 -> Maybe Type.Canonical
370 -> (Fields, Maybe String)
371flattenRecord fields ext =
372 first Map.fromList (flattenRecordHelp fields ext)
376 :: [(String, Type.Canonical)]
377 -> Maybe Type.Canonical
378 -> ([(String, Type.Canonical)], Maybe String)
379flattenRecordHelp fields ext =
387 Just (Type.Record subFields subExt) ->
388 flattenRecordHelp (fields ++ subFields) subExt
390 Just (Type.Aliased _ args tipe) ->
391 flattenRecordHelp fields (Just (Type.dealias args tipe))
394 error "Trying to flatten ill-formed record."
401docType :: Localizer -> Type.Canonical -> Doc
402docType localizer tipe =
405 docLambda (map (docType localizer) (Type.collectLambdas tipe))
411 varToDoc localizer name
413 Type.App (Type.Type name) args ->
414 docApp localizer name (map (docType localizer) args)
417 error "type applications should start with a type atom"
419 Type.Record outerFields outerExt ->
422 flattenRecordHelp outerFields outerExt
425 (map (text *** docType localizer) fields)
428 Type.Aliased name args _ ->
429 docApp localizer name (map (docType localizer . snd) args)
433docLambda :: [Doc] -> Doc
437 error "cannot call docLambda with an empty list"
440 sep (arg : map (text "->" <+>) rest)
444docApp :: Localizer -> Var.Canonical -> [Doc] -> Doc
445docApp localizer name args =
446 if Var.isTuple name then
448 [ cat (zipWith (<+>) (lparen : repeat comma) args)
452 else if null args then
453 varToDoc localizer name
456 hang 4 (sep (varToDoc localizer name : args))
465docRecord :: Style -> [(Doc,Doc)] -> Maybe Doc -> Doc
466docRecord style fields maybeExt =
468 docField (name, tipe) =
469 hang 4 (sep [ name <+> colon, tipe ])
480 elision ++ map docField fields
482 case (fieldDocs, maybeExt) of
488 [ cat (zipWith (<+>) (lbrace : repeat comma) fieldDocs)
496 , 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