1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
10import qualified Data.List as List
11import qualified Data.Map as Map
12import qualified Data.Maybe as Maybe
13import qualified Data.Name as Name
16import Deps.Diff (PackageChanges(..), ModuleChanges(..), Changes(..))
17import qualified Deps.Diff as DD
18import qualified Deps.Registry as Registry
19import qualified Elm.Compiler.Type as Type
20import qualified Elm.Details as Details
21import qualified Elm.Docs as Docs
22import qualified Elm.Magnitude as M
23import qualified Elm.Package as Pkg
24import qualified Elm.Version as V
26import qualified Reporting
27import Reporting.Doc ((<>), (<+>))
28import qualified Reporting.Doc as D
29import qualified Reporting.Exit as Exit
30import qualified Reporting.Exit.Help as Help
31import qualified Reporting.Render.Type.Localizer as L
32import qualified Reporting.Task as Task
42 | CodeVsExactly V.Version
43 | LocalInquiry V.Version V.Version
44 | GlobalInquiry Pkg.Name V.Version V.Version
47run :: Args -> () -> IO ()
49 Reporting.attempt Exit.diffToReport $
61 { _root :: Maybe FilePath
62 , _cache :: Stuff.PackageCache
63 , _manager :: Http.Manager
64 , _registry :: Registry.Registry
70 do root <- Task.io $ Stuff.findRoot
71 cache <- Task.io $ Stuff.getPackageCache
72 manager <- Task.io $ Http.getManager
73 registry <- Task.eio Exit.DiffBadRegistry $ Registry.latest manager cache
74 return (Env root cache manager registry)
85diff :: Env -> Args -> Task ()
86diff env@(Env _ _ _ registry) args =
88 GlobalInquiry name v1 v2 ->
89 case Registry.getVersions' name registry of
91 do oldDocs <- getDocs env name vsns (min v1 v2)
92 newDocs <- getDocs env name vsns (max v1 v2)
93 writeDiff oldDocs newDocs
96 Task.throw $ Exit.DiffUnknownPackage name suggestions
99 do (_, name, vsns) <- getDetails env
100 oldDocs <- getDocs env name vsns (min v1 v2)
101 newDocs <- getDocs env name vsns (max v1 v2)
102 writeDiff oldDocs newDocs
105 do (details, name, vsns) <- getDetails env
106 oldDocs <- getLatestDocs env name vsns
107 newDocs <- Build.generateDocs details
108 writeDiff oldDocs newDocs
110 CodeVsExactly version ->
111 do (details, name, vsns) <- getDetails env
112 oldDocs <- getDocs env name vsns version
113 newDocs <- Build.generateDocs details
114 writeDiff oldDocs newDocs
121getDocs :: Env -> Pkg.Name -> Registry.KnownVersions -> V.Version -> Task Docs.Documentation
122getDocs (Env _ cache manager _) name (Registry.KnownVersions latest previous) version =
123 if latest == version || elem version previous
124 then Task.eio Exit.DiffDocsProblem $ DD.getDocs cache manager name version
125 else Task.throw $ Exit.DiffUnknownVersion name version (latest:previous)
128getLatestDocs :: Env -> Pkg.Name -> Registry.KnownVersions -> Task Docs.Documentation
129getLatestDocs (Env _ cache manager _) name (Registry.KnownVersions latest _) =
130 Task.eio Exit.DiffDocsProblem $ DD.getDocs cache manager name latest
137getDetails :: Env -> Task (Details.Details, Pkg.Name, Registry.KnownVersions)
138getDetails (Env maybeRoot _ _ registry) =
141 Task.throw $ Exit.DiffNoOutline
144 do details <- Task.eio Exit.DiffBadDetails $ Details.load Reporting.silent root
145 case Details._outline details of
146 Details.ValidApp _ ->
147 Task.throw $ Exit.DiffApplication
149 Details.ValidPkg pkg _ ->
150 case Registry.getVersions pkg registry of
152 return (details, pkg, vsns)
155 Task.throw Exit.DiffUnpublished
162writeDiff :: Docs.Documentation -> Docs.Documentation -> Task ()
163writeDiff oldDocs newDocs =
165 changes = DD.diff oldDocs newDocs
166 localizer = L.fromNames (Map.union oldDocs newDocs)
168 Task.io $ Help.toStdout $ toDoc localizer changes <> "\n"
175toDoc :: L.Localizer -> PackageChanges -> D.Doc
176toDoc localizer changes@(PackageChanges added changed removed) =
177 if null added && Map.null changed && null removed then
178 "No API changes detected, so this is a" <+> D.green "PATCH" <+> "change."
182 D.fromChars (M.toChars (DD.toMagnitude changes))
185 "This is a" <+> D.green magDoc <+> "change."
188 if null added then [] else
189 [ Chunk "ADDED MODULES" M.MINOR $
190 D.vcat $ map D.fromName added
194 if null removed then [] else
195 [ Chunk "REMOVED MODULES" M.MAJOR $
196 D.vcat $ map D.fromName removed
200 addedChunk ++ removedChunk ++ map (changesToChunk localizer) (Map.toList changed)
202 D.vcat (header : "" : map chunkToDoc chunks)
208 , _magnitude :: M.Magnitude
213chunkToDoc :: Chunk -> D.Doc
214chunkToDoc (Chunk title magnitude details) =
217 "----" <+> D.fromChars title <+> "-" <+> D.fromChars (M.toChars magnitude) <+> "----"
228changesToChunk :: L.Localizer -> (Name.Name, ModuleChanges) -> Chunk
229changesToChunk localizer (name, changes@(ModuleChanges unions aliases values binops)) =
232 DD.moduleChangeMagnitude changes
234 (unionAdd, unionChange, unionRemove) =
235 changesToDocTriple (unionToDoc localizer) unions
237 (aliasAdd, aliasChange, aliasRemove) =
238 changesToDocTriple (aliasToDoc localizer) aliases
240 (valueAdd, valueChange, valueRemove) =
241 changesToDocTriple (valueToDoc localizer) values
243 (binopAdd, binopChange, binopRemove) =
244 changesToDocTriple (binopToDoc localizer) binops
246 Chunk (Name.toChars name) magnitude $
247 D.vcat $ List.intersperse "" $ Maybe.catMaybes $
248 [ changesToDoc "Added" unionAdd aliasAdd valueAdd binopAdd
249 , changesToDoc "Removed" unionRemove aliasRemove valueRemove binopRemove
250 , changesToDoc "Changed" unionChange aliasChange valueChange binopChange
254changesToDocTriple :: (k -> v -> D.Doc) -> Changes k v -> ([D.Doc], [D.Doc], [D.Doc])
255changesToDocTriple entryToDoc (Changes added changed removed) =
257 indented (name, value) =
258 D.indent 4 (entryToDoc name value)
260 diffed (name, (oldValue, newValue)) =
262 [ " - " <> entryToDoc name oldValue
263 , " + " <> entryToDoc name newValue
267 ( map indented (Map.toList added)
268 , map diffed (Map.toList changed)
269 , map indented (Map.toList removed)
273changesToDoc :: String -> [D.Doc] -> [D.Doc] -> [D.Doc] -> [D.Doc] -> Maybe D.Doc
274changesToDoc categoryName unions aliases values binops =
275 if null unions && null aliases && null values && null binops then
280 D.fromChars categoryName <> ":" : unions ++ aliases ++ binops ++ values
283unionToDoc :: L.Localizer -> Name.Name -> Docs.Union -> D.Doc
284unionToDoc localizer name (Docs.Union _ tvars ctors) =
287 "type" <+> D.fromName name <+> D.hsep (map D.fromName tvars)
289 ctorDoc (ctor, tipes) =
290 typeDoc localizer (Type.Type ctor tipes)
292 D.hang 4 (D.sep (setup : zipWith (<+>) ("=" : repeat "|") (map ctorDoc ctors)))
295aliasToDoc :: L.Localizer -> Name.Name -> Docs.Alias -> D.Doc
296aliasToDoc localizer name (Docs.Alias _ tvars tipe) =
299 "type" <+> "alias" <+> D.hsep (map D.fromName (name:tvars)) <+> "="
301 D.hang 4 (D.sep [ declaration, typeDoc localizer tipe ])
304valueToDoc :: L.Localizer -> Name.Name -> Docs.Value -> D.Doc
305valueToDoc localizer name (Docs.Value _ tipe) =
306 D.hang 4 $ D.sep [ D.fromName name <+> ":", typeDoc localizer tipe ]
309binopToDoc :: L.Localizer -> Name.Name -> Docs.Binop -> D.Doc
310binopToDoc localizer name (Docs.Binop _ tipe associativity (Docs.Precedence n)) =
311 "(" <> D.fromName name <> ")" <+> ":" <+> typeDoc localizer tipe <> D.black details
314 " (" <> D.fromName assoc <> "/" <> D.fromInt n <> ")"
317 case associativity of
320 Docs.Right -> "right"
323typeDoc :: L.Localizer -> Type.Type -> D.Doc
324typeDoc localizer tipe =
325 Type.toDoc localizer Type.None tipe
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