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 -> Style -> Type.Canonical -> Type.Canonical -> (Doc,Doc)
37diffToDocs localizer style leftType rightType =
38 case diff localizer style 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
166diff :: Localizer -> Style -> Type.Canonical -> Type.Canonical -> Diff Doc
167diff localizer style leftType rightType =
169 go = diff localizer style
171 case (leftType, rightType) of
172 (Type.Lambda _ _, Type.Lambda _ _) ->
174 leftParts = Type.collectLambdas leftType
175 rightParts = Type.collectLambdas rightType
177 if length leftParts /= length rightParts then
179 (docLambda (map (docType localizer) leftParts))
180 (docLambda (map (docType localizer) rightParts))
183 docLambda <$> sequenceA (zipWith go leftParts rightParts)
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 leftName (map (docType localizer) leftArgs))
195 (docApp localizer rightName (map (docType localizer) rightArgs))
198 docApp localizer leftName
199 <$> sequenceA (zipWith go leftArgs rightArgs)
201 (Type.App _ _, Type.App _ _) ->
202 error "Type applications without concrete names should not get here."
204 (Type.Record outerLeftFields outerLeftExt, Type.Record outerRightFields outerRightExt) ->
206 (leftFields, leftExt) =
207 flattenRecord outerLeftFields outerLeftExt
209 (rightFields, rightExt) =
210 flattenRecord outerRightFields outerRightExt
214 diffRecordElide localizer leftFields leftExt rightFields rightExt
219 (Type.Aliased leftName leftArgs _, Type.Aliased rightName rightArgs _) | leftName == rightName ->
220 docApp localizer leftName
221 <$> sequenceA (zipWith go (map snd leftArgs) (map snd rightArgs))
223 (Type.Aliased _ args real, _) ->
224 go (Type.dealias args real) rightType
226 (_, Type.Aliased _ args real) ->
227 go leftType (Type.dealias args real)
231 (docType localizer leftType)
232 (docType localizer rightType)
235difference :: Doc -> Doc -> Diff Doc
236difference leftDoc rightDoc =
237 Diff (dullyellow leftDoc) (dullyellow rightDoc)
244diffRecordElide :: Localizer -> Fields -> Maybe String -> Fields -> Maybe String -> Diff Doc
245diffRecordElide localizer leftFields leftExt rightFields rightExt =
247 (leftOnly, both, rightOnly) =
248 vennDiagram leftFields rightFields
250 if Map.null leftOnly && Map.null rightOnly then
253 Map.map (uncurry (diff localizer Elide)) both
255 case partitionDiffs fieldDiffs of
259 docRecord Full (map (first text) sames)
261 if leftExt == rightExt then
262 Same (mkRecord (fmap text leftExt))
266 (mkRecord (fmap (dullyellow . text) leftExt))
267 (mkRecord (fmap (dullyellow . text) rightExt))
271 (lefts, rights) = unzipDiffs diffs
272 style = if null sames then Full else Elide
275 (docRecord style lefts (fmap text leftExt))
276 (docRecord style rights (fmap text rightExt))
281 analyzeFields (Map.keys leftOnly) (Map.keys rightOnly)
284 if Map.null both then Full else Elide
287 (docRecord style lefts (fmap text leftExt))
288 (docRecord style rights (fmap text rightExt))
291unzipDiffs :: [(String, (Doc, Doc))] -> ( [(Doc, Doc)], [(Doc, Doc)] )
292unzipDiffs diffPairs =
294 unzipHelp (name, (left, right)) =
295 (,) (text name, left) (text name, right)
297 unzip (map unzipHelp diffPairs)
301-- ANALYZE FIELD NAMES FOR SIMILAR NAMES
304analyzeFields :: [String] -> [String] -> ( [(Doc, Doc)], [(Doc, Doc)] )
305analyzeFields leftOnly rightOnly =
308 findPotentialTypos leftOnly rightOnly
311 ( func (text name), text "…" )
313 mkFieldWith counts name =
314 mkField (if Set.member name counts then dullyellow else id) name
316 case vetTypos potentialTypos of
317 Just (leftTypos, rightTypos) ->
318 ( map (mkFieldWith leftTypos) leftOnly
319 , map (mkFieldWith rightTypos) rightOnly
323 ( map (mkField dullyellow) leftOnly
324 , map (mkField dullyellow) rightOnly
328findPotentialTypos :: [String] -> [String] -> [(String, String)]
329findPotentialTypos leftOnly rightOnly =
332 map ((,) leftName) (filter ((==1) . Help.distance leftName) rightOnly)
334 concatMap veryNear leftOnly
337vetTypos :: [(String, String)] -> Maybe (Set.Set String, Set.Set String)
338vetTypos potentialTypos =
340 tallyNames (ln, rn) (lc, rc) =
341 ( Map.insertWith (+) ln 1 lc
342 , Map.insertWith (+) rn 1 rc
345 (leftCounts, rightCounts) =
346 foldr tallyNames (Map.empty, Map.empty) potentialTypos
349 Map.foldr (\n unique -> n < 2 && unique) True counts
351 if allUnique leftCounts && allUnique rightCounts then
352 Just (Map.keysSet leftCounts, Map.keysSet rightCounts)
358-- RECORD DIFFS HELPERS
362 Map.Map String Type.Canonical
366 Map.Map String (Type.Canonical,Type.Canonical)
369vennDiagram :: Fields -> Fields -> (Fields, DoubleFields, Fields)
370vennDiagram leftFields rightFields =
371 ( Map.difference leftFields rightFields
372 , Map.intersectionWith (,) leftFields rightFields
373 , Map.difference rightFields leftFields
378 :: [(String, Type.Canonical)]
379 -> Maybe Type.Canonical
380 -> (Fields, Maybe String)
381flattenRecord fields ext =
382 first Map.fromList (flattenRecordHelp fields ext)
386 :: [(String, Type.Canonical)]
387 -> Maybe Type.Canonical
388 -> ([(String, Type.Canonical)], Maybe String)
389flattenRecordHelp fields ext =
397 Just (Type.Record subFields subExt) ->
398 flattenRecordHelp (fields ++ subFields) subExt
400 Just (Type.Aliased _ args tipe) ->
401 flattenRecordHelp fields (Just (Type.dealias args tipe))
404 error "Trying to flatten ill-formed record."
411docType :: Localizer -> Type.Canonical -> Doc
412docType localizer tipe =
415 docLambda (map (docType localizer) (Type.collectLambdas tipe))
421 varToDoc localizer name
423 Type.App (Type.Type name) args ->
424 docApp localizer name (map (docType localizer) args)
427 error "type applications should start with a type atom"
429 Type.Record outerFields outerExt ->
432 flattenRecordHelp outerFields outerExt
435 (map (text *** docType localizer) fields)
438 Type.Aliased name args _ ->
439 docApp localizer name (map (docType localizer . snd) args)
443docLambda :: [Doc] -> Doc
447 error "cannot call docLambda with an empty list"
450 sep (arg : map (text "->" <+>) rest)
454docApp :: Localizer -> Var.Canonical -> [Doc] -> Doc
455docApp localizer name args =
456 if Var.isTuple name then
458 [ cat (zipWith (<+>) (lparen : repeat comma) args)
462 else if null args then
463 varToDoc localizer name
466 hang 4 (sep (varToDoc localizer name : args))
470docRecord :: Style -> [(Doc,Doc)] -> Maybe Doc -> Doc
471docRecord style fields maybeExt =
473 docField (name, tipe) =
474 hang 4 (sep [ name <+> colon, tipe ])
485 elision ++ map docField fields
487 case (fieldDocs, maybeExt) of
493 [ cat (zipWith (<+>) (lbrace : repeat comma) fieldDocs)
501 , 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