1{-# LANGUAGE OverloadedStrings #-}
8 , moduleChangeMagnitude
16import Control.Monad (zipWithM)
17import Data.Function (on)
18import qualified Data.List as List
19import qualified Data.Map as Map
20import qualified Data.Set as Set
21import qualified Data.Text as Text
23import qualified Elm.Compiler.Module as Module
24import qualified Elm.Compiler.Type as Type
25import qualified Elm.Docs as Docs
26import qualified Elm.Name as N
27import qualified Elm.Package as Pkg
36 { _modules_added :: [Module.Raw]
37 , _modules_changed :: Map.Map Module.Raw ModuleChanges
38 , _modules_removed :: [Module.Raw]
44 { _unions :: Changes N.Name Docs.Union
45 , _aliases :: Changes N.Name Docs.Alias
46 , _values :: Changes N.Name Docs.Value
47 , _binops :: Changes N.Name Docs.Binop
53 { _added :: Map.Map k v
54 , _changed :: Map.Map k (v,v)
55 , _removed :: Map.Map k v
59getChanges :: (Ord k) => (v -> v -> Bool) -> Map.Map k v -> Map.Map k v -> Changes k v
60getChanges isEquivalent old new =
62 overlap = Map.intersectionWith (,) old new
63 changed = Map.filter (not . uncurry isEquivalent) overlap
65 Changes (Map.difference new old) changed (Map.difference old new)
72diff :: Docs.Documentation -> Docs.Documentation -> PackageChanges
75 filterOutPatches chngs =
76 Map.filter (\chng -> moduleChangeMagnitude chng /= PATCH) chngs
78 (Changes added changed removed) =
79 getChanges (\_ _ -> False) oldDocs newDocs
83 (filterOutPatches (Map.map diffModule changed))
88diffModule :: (Docs.Module, Docs.Module) -> ModuleChanges
89diffModule (Docs.Module _ _ u1 a1 v1 b1, Docs.Module _ _ u2 a2 v2 b2) =
91 (getChanges isEquivalentUnion u1 u2)
92 (getChanges isEquivalentAlias a1 a2)
93 (getChanges isEquivalentValue v1 v2)
94 (getChanges isEquivalentBinop b1 b2)
101isEquivalentUnion :: Docs.Union -> Docs.Union -> Bool
102isEquivalentUnion (Docs.Union oldComment oldVars oldCtors) (Docs.Union newComment newVars newCtors) =
103 length oldCtors == length newCtors
104 && and (zipWith (==) (map fst oldCtors) (map fst newCtors))
105 && and (Map.elems (Map.intersectionWith equiv (Map.fromList oldCtors) (Map.fromList newCtors)))
107 equiv :: [Type.Type] -> [Type.Type] -> Bool
108 equiv oldTypes newTypes =
113 (map (Docs.Alias oldComment oldVars) oldTypes)
114 (map (Docs.Alias newComment newVars) newTypes)
116 length oldTypes == length newTypes
120isEquivalentAlias :: Docs.Alias -> Docs.Alias -> Bool
121isEquivalentAlias (Docs.Alias _ oldVars oldType) (Docs.Alias _ newVars newType) =
122 case diffType oldType newType of
127 length oldVars == length newVars
128 && isEquivalentRenaming (zip oldVars newVars ++ renamings)
131isEquivalentValue :: Docs.Value -> Docs.Value -> Bool
132isEquivalentValue (Docs.Value c1 t1) (Docs.Value c2 t2) =
133 isEquivalentAlias (Docs.Alias c1 [] t1) (Docs.Alias c2 [] t2)
136isEquivalentBinop :: Docs.Binop -> Docs.Binop -> Bool
137isEquivalentBinop (Docs.Binop c1 t1 a1 p1) (Docs.Binop c2 t2 a2 p2) =
138 isEquivalentAlias (Docs.Alias c1 [] t1) (Docs.Alias c2 [] t2)
147diffType :: Type.Type -> Type.Type -> Maybe [(N.Name,N.Name)]
148diffType oldType newType =
149 case (oldType, newType) of
150 (Type.Var oldName, Type.Var newName) ->
151 Just [(oldName, newName)]
153 (Type.Lambda a b, Type.Lambda a' b') ->
158 (Type.Type oldName oldArgs, Type.Type newName newArgs) ->
159 if not (isSameName oldName newName) || length oldArgs /= length newArgs then
162 concat <$> zipWithM diffType oldArgs newArgs
164 (Type.Record fields maybeExt, Type.Record fields' maybeExt') ->
165 case (maybeExt, maybeExt') of
172 (Nothing, Nothing) ->
173 diffFields fields fields'
175 (Just oldExt, Just newExt) ->
176 (:) (oldExt, newExt) <$> diffFields fields fields'
178 (Type.Unit, Type.Unit) ->
181 (Type.Tuple a b cs, Type.Tuple x y zs) ->
182 if length cs /= length zs then
185 do aVars <- diffType a x
186 bVars <- diffType b y
187 cVars <- concat <$> zipWithM diffType cs zs
188 return (aVars ++ bVars ++ cVars)
194-- handle very old docs that do not use qualified names
195isSameName :: N.Name -> N.Name -> Bool
196isSameName oldFullName newFullName =
199 reverse (Text.splitOn "." (N.toText name))
201 case ( dedot oldFullName, dedot newFullName ) of
202 (oldName:[], newName:_) ->
205 (oldName:_, newName:[]) ->
209 oldFullName == newFullName
212diffFields :: [(N.Name, Type.Type)] -> [(N.Name, Type.Type)] -> Maybe [(N.Name,N.Name)]
213diffFields oldRawFields newRawFields =
215 sort = List.sortBy (compare `on` fst)
216 oldFields = sort oldRawFields
217 newFields = sort newRawFields
219 if length oldRawFields /= length newRawFields then
222 else if or (zipWith ((/=) `on` fst) oldFields newFields) then
226 concat <$> zipWithM (diffType `on` snd) oldFields newFields
233isEquivalentRenaming :: [(N.Name,N.Name)] -> Bool
234isEquivalentRenaming varPairs =
237 Map.toList (foldr insert Map.empty varPairs)
239 insert (old,new) dict =
240 Map.insertWith (++) old [new] dict
248 if all (new ==) rest then
254 length list == Set.size (Set.fromList list)
256 case mapM verify renamings of
260 Just verifiedRenamings ->
261 all compatibleVars verifiedRenamings
263 allUnique (map snd verifiedRenamings)
266compatibleVars :: (N.Name, N.Name) -> Bool
267compatibleVars (old, new) =
268 case (categorizeVar old, categorizeVar new) of
269 (CompAppend, CompAppend) -> True
270 (Comparable, Comparable) -> True
271 (Appendable, Appendable) -> True
272 (Number , Number ) -> True
273 (Number , Comparable) -> True
288categorizeVar :: N.Name -> TypeVarCategory
290 | N.startsWith "compappend" name = CompAppend
291 | N.startsWith "comparable" name = Comparable
292 | N.startsWith "appendable" name = Appendable
293 | N.startsWith "number" name = Number
308magnitudeToString :: Magnitude -> String
309magnitudeToString magnitude =
321bump :: PackageChanges -> Pkg.Version -> Pkg.Version
322bump changes version =
323 case toMagnitude changes of
325 Pkg.bumpPatch version
328 Pkg.bumpMinor version
331 Pkg.bumpMajor version
334toMagnitude :: PackageChanges -> Magnitude
335toMagnitude (PackageChanges added changed removed) =
337 addMag = if null added then PATCH else MINOR
338 removeMag = if null removed then PATCH else MAJOR
339 changeMags = map moduleChangeMagnitude (Map.elems changed)
341 maximum (addMag : removeMag : changeMags)
344moduleChangeMagnitude :: ModuleChanges -> Magnitude
345moduleChangeMagnitude (ModuleChanges unions aliases values binops) =
347 [ changeMagnitude unions
348 , changeMagnitude aliases
349 , changeMagnitude values
350 , changeMagnitude binops
354changeMagnitude :: Changes k v -> Magnitude
355changeMagnitude (Changes added changed removed) =
356 if Map.size removed > 0 || Map.size changed > 0 then
359 else if Map.size added > 0 then
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