1
1
mirror of https://github.com/tweag/ormolu.git synced 2024-09-11 13:16:13 +03:00

Support explicit package names in module re-exports in .ormolu

Support explicit mention of target package name in module re-exports. Even
if the exported package is not specified as a direct dependency of the
component being formatted it will be taken into account correctly.
This commit is contained in:
Mark Karpov 2023-06-02 11:27:07 +02:00 committed by Mark Karpov
parent fe98934c05
commit 9fabd8cfe1
13 changed files with 71 additions and 25 deletions

View File

@ -1,4 +1,4 @@
## Unreleased
## Ormolu 0.7.1.0
* Include `base` fixity information when formatting a Haskell file that's
not mentioned in an existing cabal file. [Issue
@ -9,6 +9,11 @@
* Ormolu is now aware of more common module re-exports by default.
* Support explicit mention of target package name in module re-exports. Even
if the exported package is not specified as a direct dependency of the
component being formatted it will still be taken into account correctly.
[Issue 1037](https://github.com/tweag/ormolu/issues/1037).
## Ormolu 0.7.0.0
* Inference of operator fixity information is now more precise and takes

View File

@ -200,11 +200,12 @@ Here is an example:
```haskell
module Control.Lens exports Control.Lens.At
module Control.Lens exports Control.Lens.Lens
module Control.Lens exports "lens" Control.Lens.Lens
```
Module re-export declarations can be mixed freely with fixity overrides, as
long as each declaration is on its own line.
long as each declaration is on its own line. As of Ormolu 0.7.1.0 explicit
package names are allowed in re-export declarations (see the example above).
Finally, all of the above-mentioned parameters can be controlled from the
command line:

View File

@ -18,6 +18,7 @@ import Data.Set qualified as Set
import Data.Text.IO qualified as TIO
import Data.Version (showVersion)
import Distribution.ModuleName (ModuleName)
import Distribution.Types.PackageName (PackageName)
import Language.Haskell.TH.Env (envQ)
import Options.Applicative
import Ormolu
@ -392,7 +393,8 @@ parseFixityDeclaration :: ReadM [(OpName, FixityInfo)]
parseFixityDeclaration = eitherReader parseFixityDeclarationStr
-- | Parse a module reexport declaration.
parseModuleReexportDeclaration :: ReadM (ModuleName, NonEmpty ModuleName)
parseModuleReexportDeclaration ::
ReadM (ModuleName, NonEmpty (Maybe PackageName, ModuleName))
parseModuleReexportDeclaration = eitherReader parseModuleReexportDeclarationStr
-- | Parse 'ColorMode'.

View File

@ -90,7 +90,7 @@ ormolu cfgWithIndices path originalInput = do
cfg = regionIndicesToDeltas totalLines <$> cfgWithIndices
fixityMap =
packageFixityMap
(cfgDependencies cfg) -- memoized on the set of dependencies
(overapproximatedDependencies cfg) -- memoized on the set of dependencies
(warnings, result0) <-
parseModule' cfg fixityMap OrmoluParsingFailed path originalInput
when (cfgDebug cfg) $ do

View File

@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
-- | Configuration options used by the tool.
@ -8,12 +9,14 @@ module Ormolu.Config
RegionDeltas (..),
SourceType (..),
defaultConfig,
overapproximatedDependencies,
regionIndicesToDeltas,
DynOption (..),
dynOptionToLocatedStr,
)
where
import Data.Map.Strict qualified as Map
import Data.Set (Set)
import Data.Set qualified as Set
import Distribution.Types.PackageName (PackageName)
@ -94,6 +97,21 @@ defaultConfig =
}
}
-- | Return all dependencies of the module. This includes both the declared
-- dependencies of the component we are working with and all potential
-- module re-export targets.
overapproximatedDependencies :: Config region -> Set PackageName
overapproximatedDependencies Config {..} =
Set.union cfgDependencies potentialReexportTargets
where
potentialReexportTargets =
Set.fromList
. concatMap toTargetPackages
$ Map.elems (unModuleReexports cfgModuleReexports)
toTargetPackages = concatMap $ \case
(Nothing, _) -> []
(Just x, _) -> [x]
-- | Convert 'RegionIndices' into 'RegionDeltas'.
regionIndicesToDeltas ::
-- | Total number of lines in the input

View File

@ -83,9 +83,9 @@ applyModuleReexports (ModuleReexports reexports) imports = imports >>= expand
case Map.lookup (fimportModule i) reexports of
Nothing -> pure i
Just exports ->
let exportToImport mmodule =
let exportToImport (mpackageName, mmodule) =
i
{ fimportPackage = Nothing,
{ fimportPackage = mpackageName,
fimportModule = mmodule
}
in NE.toList exports >>= expand . exportToImport

View File

@ -166,7 +166,7 @@ defaultFixityOverrides = FixityOverrides Map.empty
-- | Module re-exports
newtype ModuleReexports = ModuleReexports
{ unModuleReexports :: Map ModuleName (NonEmpty ModuleName)
{ unModuleReexports :: Map ModuleName (NonEmpty (Maybe PackageName, ModuleName))
}
deriving stock (Eq, Show)
@ -175,7 +175,8 @@ defaultModuleReexports :: ModuleReexports
defaultModuleReexports =
ModuleReexports . Map.fromList $
[ ( "Control.Lens",
NE.fromList
l
"lens"
[ "Control.Lens.At",
"Control.Lens.Cons",
"Control.Lens.Each",
@ -201,12 +202,14 @@ defaultModuleReexports =
]
),
( "Servant",
NE.fromList
l
"servant"
[ "Servant.API"
]
),
( "Optics",
NE.fromList
l
"optics"
[ "Optics.Fold",
"Optics.Operators",
"Optics.IxAffineFold",
@ -216,11 +219,14 @@ defaultModuleReexports =
]
),
( "Test.Hspec",
NE.fromList
l
"hspec-expectations"
[ "Test.Hspec.Expectations"
]
)
]
where
l packageName xs = (Just packageName,) <$> NE.fromList xs
-- | Fixity information that is specific to a package being formatted. It
-- requires module-specific imports in order to be usable.

View File

@ -70,7 +70,7 @@ parseModuleReexportDeclaration ::
-- | Parse result
Either
(ParseErrorBundle Text Void)
(ModuleName, NonEmpty ModuleName)
(ModuleName, NonEmpty (Maybe PackageName, ModuleName))
parseModuleReexportDeclaration = runParser (pModuleReexport <* eof) ""
pDotOrmolu :: Parser (FixityOverrides, ModuleReexports)
@ -128,7 +128,7 @@ pOperator = OpName <$> (tickedOperator <|> normalOperator)
normalOperator =
takeWhile1P (Just "operator character") isOperatorConstituent
pModuleReexport :: Parser (ModuleName, NonEmpty ModuleName)
pModuleReexport :: Parser (ModuleName, NonEmpty (Maybe PackageName, ModuleName))
pModuleReexport = do
void (string "module")
hidden hspace1
@ -136,9 +136,12 @@ pModuleReexport = do
hidden hspace1
void (string "exports")
hidden hspace1
mexportedPackage <-
optional $
between (char '\"') (char '\"') pPackageName <* hidden hspace1
exportedModule <- pModuleName
hidden hspace
return (exportingModule, NE.singleton exportedModule)
return (exportingModule, NE.singleton (mexportedPackage, exportedModule))
pModuleName :: Parser ModuleName
pModuleName =

View File

@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
@ -20,6 +21,7 @@ import Data.Text.Lazy.Builder qualified as B
import Data.Text.Lazy.Builder.Int qualified as B
import Distribution.ModuleName (ModuleName)
import Distribution.ModuleName qualified as ModuleName
import Distribution.Types.PackageName
import Ormolu.Fixity
-- | Print out a textual representation of an @.ormolu@ file.
@ -53,19 +55,23 @@ renderSingleFixityOverride (OpName operator, FixityInfo {..}) =
isTickedOperator = maybe True (Char.isLetter . fst) . T.uncons
renderSingleModuleReexport ::
(ModuleName, NonEmpty ModuleName) ->
(ModuleName, NonEmpty (Maybe PackageName, ModuleName)) ->
Builder
renderSingleModuleReexport (exportingModule, exports) =
sconcat (renderSingle <$> exports)
where
renderSingle exportedModule =
renderSingle (mexportedPackage, exportedModule) =
mconcat
[ "module ",
renderModuleName exportingModule,
" exports ",
renderOptionalPackageName mexportedPackage,
renderModuleName exportedModule,
"\n"
]
renderOptionalPackageName = \case
Nothing -> mempty
Just x -> "\"" <> B.fromString (unPackageName x) <> "\" "
renderModuleName :: ModuleName -> Builder
renderModuleName = B.fromString . intercalate "." . ModuleName.components

View File

@ -16,6 +16,7 @@ import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import Distribution.ModuleName (ModuleName)
import Distribution.Types.PackageName (PackageName)
import Ormolu.Exception
import Ormolu.Fixity
import Ormolu.Fixity.Parser
@ -82,6 +83,6 @@ parseModuleReexportDeclarationStr ::
-- | Input to parse
String ->
-- | Parse result
Either String (ModuleName, NonEmpty ModuleName)
Either String (ModuleName, NonEmpty (Maybe PackageName, ModuleName))
parseModuleReexportDeclarationStr =
first errorBundlePretty . parseModuleReexportDeclaration . T.pack

View File

@ -78,7 +78,7 @@ spec = do
""
( T.unlines
[ "module Control.Lens exports Control.Lens.Lens",
"module Control.Lens exports Control.Lens.At",
"module Control.Lens exports \"lens\" Control.Lens.At",
"module Text.Megaparsec exports Control.Monad.Combinators"
]
)
@ -93,7 +93,7 @@ spec = do
"infixl 4 <$",
"",
"",
"module Control.Lens exports Control.Lens.At",
"module Control.Lens exports \"lens\" Control.Lens.At",
"infixr 9 .",
"module Text.Megaparsec exports Control.Monad.Combinators",
"infixl 1 >>, >>=",
@ -170,7 +170,12 @@ spec = do
it "parses a re-export declaration" $
parseModuleReexportDeclaration "module Control.Lens exports Control.Lens.Lens"
`shouldParse` ( "Control.Lens",
"Control.Lens.Lens" :| []
(Nothing, "Control.Lens.Lens") :| []
)
it "parses a re-export declaration (explicit package)" $
parseModuleReexportDeclaration "module Control.Lens exports \"lens\" Control.Lens.Lens"
`shouldParse` ( "Control.Lens",
(Just "lens", "Control.Lens.Lens") :| []
)
it "fails with correct parse error (keyword wrong)" $
parseModuleReexportDeclaration "foo Control.Lens exports Control.Lens.Lens"
@ -223,10 +228,10 @@ exampleModuleReexports :: ModuleReexports
exampleModuleReexports =
ModuleReexports . Map.fromList $
[ ( "Control.Lens",
"Control.Lens.At" :| ["Control.Lens.Lens"]
(Nothing, "Control.Lens.Lens") :| [(Just "lens", "Control.Lens.At")]
),
( "Text.Megaparsec",
"Control.Monad.Combinators" :| []
(Nothing, "Control.Monad.Combinators") :| []
)
]

View File

@ -234,7 +234,7 @@ spec = do
ModuleReexports $
Map.insert
"Foo"
("Control.Lens" :| [])
((Nothing, "Control.Lens") :| [])
(unModuleReexports defaultModuleReexports)
checkFixities
["lens"]

View File

@ -50,7 +50,6 @@ checkExample srcPath' = it (fromRelFile srcPath' ++ " works") . withNiceExceptio
[ "base",
"esqueleto",
"hspec",
"hspec-expectations",
"lens",
"servant"
]