on master help sign in
Implement merging on imports
commitca83f15869cf8dd2ea5666f63fceb73df4160781 authorMark Karpov <markkarpov92@gmail.com> dateMon 22 Jun 2020, 12:25 (6 years ago) parent03e6c70
16 files changed+100−37
CHANGELOG.md+3 data/examples/import/merging-0-out.hs+3 data/examples/import/merging-0.hs+4 data/examples/import/merging-1-out.hs+2 data/examples/import/merging-1.hs+3 data/examples/import/merging-2-out.hs+2 data/examples/import/merging-2.hs+4 data/examples/import/qualified-post-out.hs+1−1 data/examples/import/qualified-prelude-out.hs+1−1 data/examples/import/simple-out.hs+2−5 expected-failures/leksah.txt+1−1 src/Ormolu/Diff.hs+3−3 src/Ormolu/Imports.hs+67−21 src/Ormolu/Printer/Meat/Module.hs+2−2 src/Ormolu/Processing/Preprocess.hs+1−2 src/Ormolu/Utils.hs+1−1
CHANGELOG.md+3
@@ -3,6 +3,9 @@
33* Imports in a import lists are now normalized: duplicate imports are
44 combined/eliminated intelligently.
55
6+* Import declarations that can be merged are now automatically merged.
7+ [Issue 414](https://github.com/tweag/ormolu/issues/414).
8+
69* The magic comments for disabling and enabling Ormolu now can encompass any
710 fragment of code provided that the remaining code after exclusion of the
811 disabled part is still syntactically correct. [Issue
data/examples/import/merging-0-out.hsnew file+3
@@ -0,0 +1,3 @@
1+import Foo
2+import Foo (bar, foo)
3+import Foo as F
data/examples/import/merging-0.hsnew file+4
@@ -0,0 +1,4 @@
1+import Foo
2+import Foo (foo)
3+import Foo (bar)
4+import Foo as F
data/examples/import/merging-1-out.hsnew file+2
@@ -0,0 +1,2 @@
1+import "bar" Foo (bar)
2+import "foo" Foo (baz, foo)
data/examples/import/merging-1.hsnew file+3
@@ -0,0 +1,3 @@
1+import "foo" Foo (foo)
2+import "bar" Foo (bar)
3+import "foo" Foo (baz)
data/examples/import/merging-2-out.hsnew file+2
@@ -0,0 +1,2 @@
1+import Foo hiding (bar4, foo2)
2+import qualified Foo (bar3, foo1)
data/examples/import/merging-2.hsnew file+4
@@ -0,0 +1,4 @@
1+import qualified Foo (foo1)
2+import Foo hiding (foo2)
3+import qualified Foo (bar3)
4+import Foo hiding (bar4)
data/examples/import/qualified-post-out.hs+1−1
@@ -1,5 +1,5 @@
11{-# LANGUAGE ImportQualifiedPost #-}
22
3−import Data.Text qualified as T
43import Data.Text qualified (a, b, c)
54import Data.Text qualified hiding (a, b, c)
5+import Data.Text qualified as T
data/examples/import/qualified-prelude-out.hs+1−1
@@ -1,4 +1,4 @@
11module P where
22
3−import qualified Prelude
43import Prelude hiding (id, (.))
4+import qualified Prelude
data/examples/import/simple-out.hs+2−5
@@ -1,8 +1,5 @@
11import Data.Text
2−import Data.Text
3−import qualified Data.Text as T
4−import qualified Data.Text (a, b, c)
5−import Data.Text (a, b, c)
6−import Data.Text hiding (a, b, c)
72import Data.Text (a, b, c)
83import Data.Text hiding (a, b, c)
4+import qualified Data.Text (a, b, c)
5+import qualified Data.Text as T
expected-failures/leksah.txt+1−1
@@ -1,5 +1,5 @@
11Formatting is not idempotent:
2− src/IDE/Pane/Modules.hs<rendered>:1189:7
2+ src/IDE/Pane/Modules.hs<rendered>:1184:7
33 before: "cr\n -- show"
44 after: "cr\n in -- show"
55Please, consider reporting the bug.
src/Ormolu/Diff.hs+3−3
@@ -14,7 +14,7 @@ import Data.Text (Text)
1414import qualified Data.Text as T
1515import qualified FastString as GHC
1616import GHC
17−import Ormolu.Imports (sortImports)
17+import Ormolu.Imports (normalizeImports)
1818import Ormolu.Parser.CommentStream
1919import Ormolu.Parser.Result
2020import Ormolu.Utils
@@ -47,8 +47,8 @@ diffParseResult
4747 } =
4848 matchIgnoringSrcSpans cstream0 cstream1
4949 <> matchIgnoringSrcSpans
50− hs0 {hsmodImports = sortImports (hsmodImports hs0)}
51− hs1 {hsmodImports = sortImports (hsmodImports hs1)}
50+ hs0 {hsmodImports = normalizeImports (hsmodImports hs0)}
51+ hs1 {hsmodImports = normalizeImports (hsmodImports hs1)}
5252
5353-- | Compare two values for equality disregarding differences in 'SrcSpan's
5454-- and the ordering of import lists.
src/Ormolu/Imports.hs+67−21
@@ -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]
src/Ormolu/Printer/Meat/Module.hs+2−2
@@ -11,7 +11,7 @@ where
1111import Control.Monad
1212import qualified Data.Text as T
1313import GHC
14−import Ormolu.Imports
14+import Ormolu.Imports (normalizeImports)
1515import Ormolu.Parser.CommentStream
1616import Ormolu.Parser.Pragma
1717import Ormolu.Parser.Shebang
@@ -69,7 +69,7 @@ p_hsModule mstackHeader shebangs pragmas qualifiedPost HsModule {..} = do
6969 txt "where"
7070 newline
7171 newline
72− forM_ (sortImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
72+ forM_ (normalizeImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
7373 newline
7474 switchLayout (getLoc <$> hsmodDecls) $ do
7575 p_hsDecls Free hsmodDecls
src/Ormolu/Processing/Preprocess.hs+1−2
@@ -11,8 +11,7 @@ where
1111import Control.Monad
1212import Data.Char (isSpace)
1313import qualified Data.List as L
14−import Data.Maybe (isJust)
15−import Data.Maybe (maybeToList)
14+import Data.Maybe (isJust, maybeToList)
1615import FastString
1716import Ormolu.Config (RegionDeltas (..))
1817import Ormolu.Parser.Shebang (isShebang)
src/Ormolu/Utils.hs+1−1
@@ -22,8 +22,8 @@ where
2222
2323import Data.Char (isSpace)
2424import Data.List (dropWhileEnd)
25−import qualified Data.List.NonEmpty as NE
2625import Data.List.NonEmpty (NonEmpty (..))
26+import qualified Data.List.NonEmpty as NE
2727import Data.Maybe (fromMaybe)
2828import Data.Text (Text)
2929import qualified Data.Text as T