@@ -4,48 +4,94 @@
5-- | Manipulations on import lists.
5-- | Manipulations on import lists.
6module Ormolu.Imports
6module Ormolu.Imports
7− ( sortImports,
7+ ( normalizeImports,
11import Data.Bifunctor
11import Data.Bifunctor
12import Data.Char (isAlphaNum)
12import Data.Char (isAlphaNum)
13import Data.Function (on)
13import Data.Function (on)
14−import Data.Generics (gcompare)
15import Data.List (foldl', nubBy, sortBy, sortOn)
14import Data.List (foldl', nubBy, sortBy, sortOn)
16import Data.Map.Strict (Map)
15import Data.Map.Strict (Map)
17import qualified Data.Map.Strict as M
16import qualified Data.Map.Strict as M
17+import FastString (FastString)
18import GHC hiding (GhcPs, IE)
18import GHC hiding (GhcPs, IE)
19import GHC.Hs.Extension
19import GHC.Hs.Extension
20import GHC.Hs.ImpExp (IE (..))
20import GHC.Hs.ImpExp (IE (..))
21import Ormolu.Utils (notImplemented, showOutputable)
21import Ormolu.Utils (notImplemented, showOutputable)
23−-- | Sort imports by module name. This also sorts and normalizes explicit
23+-- | Sort and normalize imports.
24−-- import lists for each declaration.
25−sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
24+normalizeImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
26−sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
25+normalizeImports =
28+ . M.fromListWith combineImports
29+ . fmap (\x -> (importId x, g x))
28− sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
29− sortImportLists = \case
30− ImportDecl {..} ->
31+ g (L l ImportDecl {..}) =
31 ImportDecl
34 ImportDecl
32 { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
35 { ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
35− XImportDecl x -> noExtCon x
38+ g _ = notImplemented "XImportDecl"
40+-- | Combine two import declarations. It should be assumed that 'ImportId's
37−-- | Compare two @'LImportDecl' 'GhcPs'@ things.
43+ LImportDecl GhcPs ->
38−compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
44+ LImportDecl GhcPs ->
39−compareIdecl (L _ m0) (L _ m1) =
45+ LImportDecl GhcPs
40− case (isPrelude n0, isPrelude n1) of
46+combineImports (L lx ImportDecl {..}) (L _ y) =
41− (False, False) -> n0 `compare` n1
47+ L
42− (True, False) -> GT
48+ lx
43− (False, True) -> LT
49+ ImportDecl
44− (True, True) -> m0 `gcompare` m1
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))))
56+combineImports _ _ = notImplemented "XImportDecl"
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,
67+ importQualified :: Bool,
68+ importImplicit :: Bool,
69+ importAs :: Maybe ModuleName,
70+ importHiding :: Maybe Bool
74+-- | Obtain an 'ImportId' for a given import.
75+importId :: LImportDecl GhcPs -> ImportId
76+importId (L _ ImportDecl {..}) =
78+ { importIsPrelude = isPrelude,
79+ importIdName = moduleName,
80+ importPkgQual = sl_fs <$> ideclPkgQual,
81+ importSource = ideclSource,
82+ importSafe = ideclSafe,
83+ importQualified = case ideclQualified of
85+ QualifiedPost -> True
86+ NotQualified -> False,
87+ importImplicit = ideclImplicit,
88+ importAs = unLoc <$> ideclAs,
89+ importHiding = fst <$> ideclHiding
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"
50-- | Normalize a collection of import\/export items.
96-- | Normalize a collection of import\/export items.
51normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]
97normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]