mirror of
https://github.com/ilyakooo0/ormolu.git
synced 2024-09-12 00:37:30 +03:00
Implement merging on imports
This commit is contained in:
parent
03e6c70a78
commit
ca83f15869
@ -3,6 +3,9 @@
|
|||||||
* Imports in a import lists are now normalized: duplicate imports are
|
* Imports in a import lists are now normalized: duplicate imports are
|
||||||
combined/eliminated intelligently.
|
combined/eliminated intelligently.
|
||||||
|
|
||||||
|
* Import declarations that can be merged are now automatically merged.
|
||||||
|
[Issue 414](https://github.com/tweag/ormolu/issues/414).
|
||||||
|
|
||||||
* The magic comments for disabling and enabling Ormolu now can encompass any
|
* The magic comments for disabling and enabling Ormolu now can encompass any
|
||||||
fragment of code provided that the remaining code after exclusion of the
|
fragment of code provided that the remaining code after exclusion of the
|
||||||
disabled part is still syntactically correct. [Issue
|
disabled part is still syntactically correct. [Issue
|
||||||
|
3
data/examples/import/merging-0-out.hs
Normal file
3
data/examples/import/merging-0-out.hs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Foo
|
||||||
|
import Foo (bar, foo)
|
||||||
|
import Foo as F
|
4
data/examples/import/merging-0.hs
Normal file
4
data/examples/import/merging-0.hs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Foo
|
||||||
|
import Foo (foo)
|
||||||
|
import Foo (bar)
|
||||||
|
import Foo as F
|
2
data/examples/import/merging-1-out.hs
Normal file
2
data/examples/import/merging-1-out.hs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import "bar" Foo (bar)
|
||||||
|
import "foo" Foo (baz, foo)
|
3
data/examples/import/merging-1.hs
Normal file
3
data/examples/import/merging-1.hs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import "foo" Foo (foo)
|
||||||
|
import "bar" Foo (bar)
|
||||||
|
import "foo" Foo (baz)
|
2
data/examples/import/merging-2-out.hs
Normal file
2
data/examples/import/merging-2-out.hs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import Foo hiding (bar4, foo2)
|
||||||
|
import qualified Foo (bar3, foo1)
|
4
data/examples/import/merging-2.hs
Normal file
4
data/examples/import/merging-2.hs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import qualified Foo (foo1)
|
||||||
|
import Foo hiding (foo2)
|
||||||
|
import qualified Foo (bar3)
|
||||||
|
import Foo hiding (bar4)
|
@ -1,5 +1,5 @@
|
|||||||
{-# LANGUAGE ImportQualifiedPost #-}
|
{-# LANGUAGE ImportQualifiedPost #-}
|
||||||
|
|
||||||
import Data.Text qualified as T
|
|
||||||
import Data.Text qualified (a, b, c)
|
import Data.Text qualified (a, b, c)
|
||||||
import Data.Text qualified hiding (a, b, c)
|
import Data.Text qualified hiding (a, b, c)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
module P where
|
module P where
|
||||||
|
|
||||||
import qualified Prelude
|
|
||||||
import Prelude hiding (id, (.))
|
import Prelude hiding (id, (.))
|
||||||
|
import qualified Prelude
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import Data.Text
|
import Data.Text
|
||||||
import Data.Text
|
import Data.Text (a, b, c)
|
||||||
import qualified Data.Text as T
|
import Data.Text hiding (a, b, c)
|
||||||
import qualified Data.Text (a, b, c)
|
import qualified Data.Text (a, b, c)
|
||||||
import Data.Text (a, b, c)
|
import qualified Data.Text as T
|
||||||
import Data.Text hiding (a, b, c)
|
|
||||||
import Data.Text (a, b, c)
|
|
||||||
import Data.Text hiding (a, b, c)
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Formatting is not idempotent:
|
Formatting is not idempotent:
|
||||||
src/IDE/Pane/Modules.hs<rendered>:1189:7
|
src/IDE/Pane/Modules.hs<rendered>:1184:7
|
||||||
before: "cr\n -- show"
|
before: "cr\n -- show"
|
||||||
after: "cr\n in -- show"
|
after: "cr\n in -- show"
|
||||||
Please, consider reporting the bug.
|
Please, consider reporting the bug.
|
||||||
|
@ -14,7 +14,7 @@ import Data.Text (Text)
|
|||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified FastString as GHC
|
import qualified FastString as GHC
|
||||||
import GHC
|
import GHC
|
||||||
import Ormolu.Imports (sortImports)
|
import Ormolu.Imports (normalizeImports)
|
||||||
import Ormolu.Parser.CommentStream
|
import Ormolu.Parser.CommentStream
|
||||||
import Ormolu.Parser.Result
|
import Ormolu.Parser.Result
|
||||||
import Ormolu.Utils
|
import Ormolu.Utils
|
||||||
@ -47,8 +47,8 @@ diffParseResult
|
|||||||
} =
|
} =
|
||||||
matchIgnoringSrcSpans cstream0 cstream1
|
matchIgnoringSrcSpans cstream0 cstream1
|
||||||
<> matchIgnoringSrcSpans
|
<> matchIgnoringSrcSpans
|
||||||
hs0 {hsmodImports = sortImports (hsmodImports hs0)}
|
hs0 {hsmodImports = normalizeImports (hsmodImports hs0)}
|
||||||
hs1 {hsmodImports = sortImports (hsmodImports hs1)}
|
hs1 {hsmodImports = normalizeImports (hsmodImports hs1)}
|
||||||
|
|
||||||
-- | Compare two values for equality disregarding differences in 'SrcSpan's
|
-- | Compare two values for equality disregarding differences in 'SrcSpan's
|
||||||
-- and the ordering of import lists.
|
-- and the ordering of import lists.
|
||||||
|
@ -4,48 +4,94 @@
|
|||||||
|
|
||||||
-- | Manipulations on import lists.
|
-- | Manipulations on import lists.
|
||||||
module Ormolu.Imports
|
module Ormolu.Imports
|
||||||
( sortImports,
|
( normalizeImports,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.Char (isAlphaNum)
|
import Data.Char (isAlphaNum)
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
import Data.Generics (gcompare)
|
|
||||||
import Data.List (foldl', nubBy, sortBy, sortOn)
|
import Data.List (foldl', nubBy, sortBy, sortOn)
|
||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
|
import FastString (FastString)
|
||||||
import GHC hiding (GhcPs, IE)
|
import GHC hiding (GhcPs, IE)
|
||||||
import GHC.Hs.Extension
|
import GHC.Hs.Extension
|
||||||
import GHC.Hs.ImpExp (IE (..))
|
import GHC.Hs.ImpExp (IE (..))
|
||||||
import Ormolu.Utils (notImplemented, showOutputable)
|
import Ormolu.Utils (notImplemented, showOutputable)
|
||||||
|
|
||||||
-- | Sort imports by module name. This also sorts and normalizes explicit
|
-- | Sort and normalize imports.
|
||||||
-- import lists for each declaration.
|
normalizeImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
|
||||||
sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
|
normalizeImports =
|
||||||
sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
|
fmap snd
|
||||||
|
. M.toAscList
|
||||||
|
. M.fromListWith combineImports
|
||||||
|
. fmap (\x -> (importId x, g x))
|
||||||
where
|
where
|
||||||
sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
|
g (L l ImportDecl {..}) =
|
||||||
sortImportLists = \case
|
L
|
||||||
ImportDecl {..} ->
|
l
|
||||||
ImportDecl
|
ImportDecl
|
||||||
{ ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
|
{ ideclHiding = second (fmap normalizeLies) <$> ideclHiding,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
XImportDecl x -> noExtCon x
|
g _ = notImplemented "XImportDecl"
|
||||||
|
|
||||||
-- | Compare two @'LImportDecl' 'GhcPs'@ things.
|
-- | Combine two import declarations. It should be assumed that 'ImportId's
|
||||||
compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
|
-- are equal.
|
||||||
compareIdecl (L _ m0) (L _ m1) =
|
combineImports ::
|
||||||
case (isPrelude n0, isPrelude n1) of
|
LImportDecl GhcPs ->
|
||||||
(False, False) -> n0 `compare` n1
|
LImportDecl GhcPs ->
|
||||||
(True, False) -> GT
|
LImportDecl GhcPs
|
||||||
(False, True) -> LT
|
combineImports (L lx ImportDecl {..}) (L _ y) =
|
||||||
(True, True) -> m0 `gcompare` m1
|
L
|
||||||
|
lx
|
||||||
|
ImportDecl
|
||||||
|
{ ideclHiding = case (ideclHiding, GHC.ideclHiding y) of
|
||||||
|
(Just (hiding, L l' xs), Just (_, L _ ys)) ->
|
||||||
|
Just (hiding, (L l' (normalizeLies (xs ++ ys))))
|
||||||
|
_ -> Nothing,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
combineImports _ _ = notImplemented "XImportDecl"
|
||||||
|
|
||||||
|
-- | Import id, a collection of all things that justify having a separate
|
||||||
|
-- import entry. This is used for merging of imports. If two imports have
|
||||||
|
-- the same 'ImportId' they can be merged.
|
||||||
|
data ImportId = ImportId
|
||||||
|
{ importIsPrelude :: Bool,
|
||||||
|
importIdName :: ModuleName,
|
||||||
|
importPkgQual :: Maybe FastString,
|
||||||
|
importSource :: Bool,
|
||||||
|
importSafe :: Bool,
|
||||||
|
importQualified :: Bool,
|
||||||
|
importImplicit :: Bool,
|
||||||
|
importAs :: Maybe ModuleName,
|
||||||
|
importHiding :: Maybe Bool
|
||||||
|
}
|
||||||
|
deriving (Eq, Ord)
|
||||||
|
|
||||||
|
-- | Obtain an 'ImportId' for a given import.
|
||||||
|
importId :: LImportDecl GhcPs -> ImportId
|
||||||
|
importId (L _ ImportDecl {..}) =
|
||||||
|
ImportId
|
||||||
|
{ importIsPrelude = isPrelude,
|
||||||
|
importIdName = moduleName,
|
||||||
|
importPkgQual = sl_fs <$> ideclPkgQual,
|
||||||
|
importSource = ideclSource,
|
||||||
|
importSafe = ideclSafe,
|
||||||
|
importQualified = case ideclQualified of
|
||||||
|
QualifiedPre -> True
|
||||||
|
QualifiedPost -> True
|
||||||
|
NotQualified -> False,
|
||||||
|
importImplicit = ideclImplicit,
|
||||||
|
importAs = unLoc <$> ideclAs,
|
||||||
|
importHiding = fst <$> ideclHiding
|
||||||
|
}
|
||||||
where
|
where
|
||||||
n0 = unLoc (ideclName m0)
|
isPrelude = moduleNameString moduleName == "Prelude"
|
||||||
n1 = unLoc (ideclName m1)
|
moduleName = unLoc ideclName
|
||||||
isPrelude = (== "Prelude") . moduleNameString
|
importId _ = notImplemented "XImportDecl"
|
||||||
|
|
||||||
-- | Normalize a collection of import\/export items.
|
-- | Normalize a collection of import\/export items.
|
||||||
normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]
|
normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]
|
||||||
|
@ -11,7 +11,7 @@ where
|
|||||||
import Control.Monad
|
import Control.Monad
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import GHC
|
import GHC
|
||||||
import Ormolu.Imports
|
import Ormolu.Imports (normalizeImports)
|
||||||
import Ormolu.Parser.CommentStream
|
import Ormolu.Parser.CommentStream
|
||||||
import Ormolu.Parser.Pragma
|
import Ormolu.Parser.Pragma
|
||||||
import Ormolu.Parser.Shebang
|
import Ormolu.Parser.Shebang
|
||||||
@ -69,7 +69,7 @@ p_hsModule mstackHeader shebangs pragmas qualifiedPost HsModule {..} = do
|
|||||||
txt "where"
|
txt "where"
|
||||||
newline
|
newline
|
||||||
newline
|
newline
|
||||||
forM_ (sortImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
|
forM_ (normalizeImports hsmodImports) (located' (p_hsmodImport qualifiedPost))
|
||||||
newline
|
newline
|
||||||
switchLayout (getLoc <$> hsmodDecls) $ do
|
switchLayout (getLoc <$> hsmodDecls) $ do
|
||||||
p_hsDecls Free hsmodDecls
|
p_hsDecls Free hsmodDecls
|
||||||
|
@ -11,8 +11,7 @@ where
|
|||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
import Data.Maybe (isJust)
|
import Data.Maybe (isJust, maybeToList)
|
||||||
import Data.Maybe (maybeToList)
|
|
||||||
import FastString
|
import FastString
|
||||||
import Ormolu.Config (RegionDeltas (..))
|
import Ormolu.Config (RegionDeltas (..))
|
||||||
import Ormolu.Parser.Shebang (isShebang)
|
import Ormolu.Parser.Shebang (isShebang)
|
||||||
|
@ -22,8 +22,8 @@ where
|
|||||||
|
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
import Data.List (dropWhileEnd)
|
import Data.List (dropWhileEnd)
|
||||||
import qualified Data.List.NonEmpty as NE
|
|
||||||
import Data.List.NonEmpty (NonEmpty (..))
|
import Data.List.NonEmpty (NonEmpty (..))
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
Loading…
Reference in New Issue
Block a user