@@ -4,48 +4,94 @@
44
55-- | Manipulations on import lists.
66module Ormolu.Imports
7− ( sortImports,
7+ ( normalizeImports,
88 )
99where
1010
1111import Data.Bifunctor
1212import Data.Char (isAlphaNum)
1313import Data.Function (on)
14−import Data.Generics (gcompare)
1514import Data.List (foldl', nubBy, sortBy, sortOn)
1615import Data.Map.Strict (Map)
1716import qualified Data.Map.Strict as M
17+import FastString (FastString)
1818import GHC hiding (GhcPs, IE)
1919import GHC.Hs.Extension
2020import GHC.Hs.ImpExp (IE (..))
2121import Ormolu.Utils (notImplemented, showOutputable)
2222
23−-- | Sort imports by module name. This also sorts and normalizes explicit
24−-- import lists for each declaration.
25−sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
26−sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
23+-- | Sort and normalize imports.
24+normalizeImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
25+normalizeImports =
26+ fmap snd
27+ . M.toAscList
28+ . M.fromListWith combineImports
29+ . fmap (\x -> (importId x, g x))
2730 where
28− sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
29− sortImportLists = \case
30− ImportDecl {..} ->
31+ g (L l ImportDecl {..}) =
32+ L
33+ l
3134 ImportDecl
3235 { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
3336 ..
3437 }
35− XImportDecl x -> noExtCon x
38+ g _ = notImplemented "XImportDecl"
3639
37−-- | Compare two @'LImportDecl' 'GhcPs'@ things.
38−compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
39−compareIdecl (L _ m0) (L _ m1) =
40− case (isPrelude n0, isPrelude n1) of
41− (False, False) -> n0 `compare` n1
42− (True, False) -> GT
43− (False, True) -> LT
44− (True, True) -> m0 `gcompare` m1
40+-- | Combine two import declarations. It should be assumed that 'ImportId's
41+-- are equal.
42+combineImports ::
43+ LImportDecl GhcPs ->
44+ LImportDecl GhcPs ->
45+ LImportDecl GhcPs
46+combineImports (L lx ImportDecl {..}) (L _ y) =
47+ L
48+ lx
49+ ImportDecl
50+ { ideclHiding = case (ideclHiding, GHC.ideclHiding y) of
51+ (Just (hiding, L l' xs), Just (_, L _ ys)) ->
52+ Just (hiding, (L l' (normalizeLies (xs ++ ys))))
53+ _ -> Nothing,
54+ ..
55+ }
56+combineImports _ _ = notImplemented "XImportDecl"
57+
58+-- | Import id, a collection of all things that justify having a separate
59+-- import entry. This is used for merging of imports. If two imports have
60+-- the same 'ImportId' they can be merged.
61+data ImportId = ImportId
62+ { importIsPrelude :: Bool,
63+ importIdName :: ModuleName,
64+ importPkgQual :: Maybe FastString,
65+ importSource :: Bool,
66+ importSafe :: Bool,
67+ importQualified :: Bool,
68+ importImplicit :: Bool,
69+ importAs :: Maybe ModuleName,
70+ importHiding :: Maybe Bool
71+ }
72+ deriving (Eq, Ord)
73+
74+-- | Obtain an 'ImportId' for a given import.
75+importId :: LImportDecl GhcPs -> ImportId
76+importId (L _ ImportDecl {..}) =
77+ ImportId
78+ { importIsPrelude = isPrelude,
79+ importIdName = moduleName,
80+ importPkgQual = sl_fs <$> ideclPkgQual,
81+ importSource = ideclSource,
82+ importSafe = ideclSafe,
83+ importQualified = case ideclQualified of
84+ QualifiedPre -> True
85+ QualifiedPost -> True
86+ NotQualified -> False,
87+ importImplicit = ideclImplicit,
88+ importAs = unLoc <$> ideclAs,
89+ importHiding = fst <$> ideclHiding
90+ }
4591 where
46− n0 = unLoc (ideclName m0)
47− n1 = unLoc (ideclName m1)
48− isPrelude = (== "Prelude") . moduleNameString
92+ isPrelude = moduleNameString moduleName == "Prelude"
93+ moduleName = unLoc ideclName
94+importId _ = notImplemented "XImportDecl"
4995
5096-- | Normalize a collection of import\/export items.
5197normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]