-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Clean up module imports, split and merge modules -- -- Clean up module imports, split and merge modules. @package module-management @version 0.10 module Language.Haskell.Modules.Util.Temp -- | Adapted from withTempDirectory to work in MonadCatchIO -- instances. withTempDirectory :: MonadCatchIO m => FilePath -> String -> (FilePath -> m a) -> m a module Language.Haskell.Modules.Util.DryIO class MonadIO m => MonadDryRun m dry :: MonadDryRun m => m Bool putDry :: MonadDryRun m => Bool -> m () dryIO :: MonadDryRun m => IO () -> m () dryIO' :: MonadDryRun m => IO a -> IO a -> m a tildeBackup :: FilePath -> Maybe FilePath noBackup :: FilePath -> Maybe FilePath readFileMaybe :: FilePath -> IO (Maybe String) -- | Replace the file at path with the given text, moving the original to -- the location returned by passing path to backup. If backup is the -- identity function you're going to have a bad time. replaceFile :: MonadDryRun m => (FilePath -> Maybe FilePath) -> FilePath -> String -> m () replaceFileIfDifferent :: MonadDryRun m => FilePath -> String -> m Bool removeFileIfPresent :: MonadDryRun m => FilePath -> m () createDirectoryIfMissing :: MonadDryRun m => Bool -> String -> m () writeFile :: MonadDryRun m => FilePath -> String -> m () module Language.Haskell.Modules.Util.SrcLoc class HasSpanInfo a spanInfo :: HasSpanInfo a => a -> SrcSpanInfo srcSpan :: HasSpanInfo x => x -> SrcSpan srcLoc :: HasSpanInfo x => x -> SrcLoc endLoc :: HasSpanInfo x => x -> SrcLoc textEndLoc :: String -> SrcLoc -- | Update a SrcLoc to move it from l past the string argument. increaseSrcLoc :: String -> SrcLoc -> SrcLoc textSpan :: String -> SrcSpanInfo -- | Return the beginning portion of s which the span b thru e covers, -- assuming that the beginning of s is at position b - that is, that the -- prefix of s from (1,1) to b has already been removed. srcPairTextHead :: SrcLoc -> SrcLoc -> String -> String -- | Like srcPairTextHead, but returns the tail of s instead of the head. srcPairTextTail :: SrcLoc -> SrcLoc -> String -> String -- | Shirley there's a more efficient way to do this? srcPairTextPair :: SrcLoc -> SrcLoc -> String -> (String, String) -- | Build a tree of SrcSpanInfo makeTree :: (HasSpanInfo a, Show a, Eq a, Ord a) => Set a -> Tree a tests :: Test instance Default SrcSpan instance Default SrcSpanInfo instance Default SrcLoc instance HasSpanInfo SrcSpanInfo instance HasSpanInfo Decl instance HasSpanInfo ImportDecl instance HasSpanInfo ExportSpec instance HasSpanInfo ExportSpecList instance HasSpanInfo WarningText instance HasSpanInfo ModulePragma instance HasSpanInfo ModuleName instance HasSpanInfo ModuleHead instance HasSpanInfo a => HasSpanInfo (Tree a) instance HasSpanInfo SrcSpan module Language.Haskell.Modules.Util.Symbols -- | Do a fold over the names that are declared in a declaration (not every -- name that appears, just the ones that the declaration is causing to -- exist - what's the word for that?. Reify!) class FoldDeclared a foldDeclared :: FoldDeclared a => (Maybe Name -> r -> r) -> r -> a -> r -- | Fold over the declared members - e.g. the method names of a class -- declaration, the constructors of a data declaration. class FoldMembers a foldMembers :: FoldMembers a => (Maybe Name -> r -> r) -> r -> a -> r symbols :: FoldDeclared a => a -> Set (Maybe Name) exports :: (FoldDeclared a, FoldMembers a) => a -> [ExportSpec] imports :: (FoldDeclared a, FoldMembers a) => a -> [ImportSpec] tests :: Test instance FoldDeclared (GadtDecl l) instance FoldDeclared (FieldDecl l) instance FoldDeclared (ConDecl l) instance FoldDeclared (QualConDecl l) instance FoldMembers (Decl a) instance FoldDeclared (ExportSpec l) instance FoldDeclared (ImportSpec l) instance FoldDeclared (Name l) instance FoldDeclared (RPat a) instance FoldDeclared (PatField a) instance FoldDeclared (Pat a) instance FoldDeclared (QName a) instance FoldDeclared (Match a) instance FoldDeclared (InstHead a) instance FoldDeclared (ClassDecl a) instance FoldDeclared (DeclHead a) instance FoldDeclared (Decl a) module Language.Haskell.Modules.Common -- | Combine sortBy and groupBy groupBy' :: Ord a => (a -> a -> Ordering) -> [a] -> [[a]] mapNames :: Default a => [Name] -> [Name a] -- | Construct the base of a module path. modulePathBase :: ModuleName -> FilePath withCurrentDirectory :: FilePath -> IO a -> IO a module Language.Haskell.Modules.Util.Test repoModules :: Set ModuleName logicModules :: Set ModuleName diff :: FilePath -> FilePath -> IO (ExitCode, String, String) -- | Like diff, but ignores extra files in b. diff' :: FilePath -> FilePath -> IO (ExitCode, String, String) -- | Convenience function for building the moduVerse, searches for modules -- in a directory hierarchy. FIXME: This should be in MonadClean and use -- the value of sourceDirs to remove prefixes from the module paths. And -- then it should look at the module text to see what the module name -- really is. findModules :: FilePath -> IO (Set ModuleName) -- | Find the paths of all the files below the directory top. findPaths :: FilePath -> IO (Set FilePath) -- | IO operations predicated on the verbosity value managed by the methods -- of MonadVerbosity. Noisily increases this value and quietly decreases -- it, and the q* operations only happen when the value is greater than -- zero. module Language.Haskell.Modules.Util.QIO class MonadIO m => MonadVerbosity m getVerbosity :: MonadVerbosity m => m Int putVerbosity :: MonadVerbosity m => Int -> m () modifyVerbosity :: MonadVerbosity m => (Int -> Int) -> m () quietly :: MonadVerbosity m => m a -> m a noisily :: MonadVerbosity m => m a -> m a qIO :: MonadVerbosity m => m () -> m () qPutStr :: MonadVerbosity m => String -> m () qPutStrLn :: MonadVerbosity m => String -> m () -- | foldModule is a utility function used to implement the clean, -- split, and merge operations. module Language.Haskell.Modules.Fold type ModuleInfo = (Module, String, [Comment]) type ModuleMap = Map ModuleName ModuleInfo -- | Given the result of parseModuleWithComments and the original module -- text, this does a fold over the parsed module contents, calling the -- seven argument functions in order. Each function is passed the AST -- value, the text of the space and comments leading up to the element, -- and the text for the element. Note that not everything passed to the -- pre argument of the functions will be comments and space - for -- example, the module keyword will be passed in the pre argument -- to the ModuleName function. foldModule :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> (ImportDecl -> String -> String -> String -> r -> r) -> (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r -- | Do just the header portion of foldModule. foldHeader :: Show r => (String -> r -> r) -> (ModulePragma -> String -> String -> String -> r -> r) -> (ModuleName -> String -> String -> String -> r -> r) -> (WarningText -> String -> String -> String -> r -> r) -> ModuleInfo -> r -> r -- | Do just the exports portion of foldModule. foldExports :: Show r => (String -> r -> r) -> (ExportSpec -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r -- | Do just the imports portion of foldModule. foldImports :: Show r => (ImportDecl -> String -> String -> String -> r -> r) -> ModuleInfo -> r -> r -- | Do just the declarations portion of foldModule. foldDecls :: Show r => (Decl -> String -> String -> String -> r -> r) -> (String -> r -> r) -> ModuleInfo -> r -> r -- | This can be passed to foldModule to include the original text in the -- result echo :: Monoid m => t -> m -> m -> m -> m -> m -- | Similar to echo, but used for the two argument separator -- functions echo2 :: Monoid m => m -> m -> m -- | This can be passed to foldModule to omit the original text from the -- result. ignore :: t -> m -> m -> m -> r -> r -- | Similar to ignore, but used for the two argument separator -- functions ignore2 :: m -> r -> r tests :: Test instance Show St instance Spans (WarningText SrcSpanInfo) instance Spans (ModuleName SrcSpanInfo) instance Spans (Decl SrcSpanInfo) instance Spans (ImportDecl SrcSpanInfo) instance Spans (ModulePragma SrcSpanInfo) instance Spans (ExportSpec SrcSpanInfo) instance Spans (ExportSpecList SrcSpanInfo) instance Spans (ModuleHead SrcSpanInfo) instance Spans (Module SrcSpanInfo) -- | Functions to control the state variables of MonadClean. module Language.Haskell.Modules.Params class (MonadIO m, MonadCatchIO m, Functor m) => MonadClean m -- | Create the environment required to do import cleaning and module -- splitting/merging. This environment, StateT Params m a, is an instance -- of MonadClean. runMonadClean :: MonadCatchIO m => StateT Params m a -> m a -- | Modify the set of modules whose imports will be updated when modules -- are split or merged. No default, it is an error to run splitModules or -- catModules without first setting this. modifyModuVerse :: MonadClean m => (Set ModuleName -> Set ModuleName) -> m () -- | If this flag is set, imports that become empty are removed. Sometimes -- this will lead to errors, specifically when an instance in the removed -- import that was required is no longer be available. (Note that this -- reflects a limitation of the -ddump-minimal-imports option of -- GHC.) If this happens this flag should be set. Note that an import -- that is already empty when cleanImports runs will never be -- removed, on the assumption that it was placed there only to import -- instances. Default is True. modifyRemoveEmptyImports :: MonadClean m => (Bool -> Bool) -> m () -- | Modify the list of directories that will be searched for source files, -- in a similar way to the Hs-Source-Dirs field in a cabal file. Default -- is ["."]. modifySourceDirs :: MonadClean m => ([FilePath] -> [FilePath]) -> m () -- | Modify the extra extensions passed to the compiler and the parser. -- Default value is the list in defaultParseMode. modifyExtensions :: MonadClean m => ([Extension] -> [Extension]) -> m () -- | Modify the list of extra flags passed to GHC. Default is []. modifyHsFlags :: MonadClean m => ([String] -> [String]) -> m () -- | Controls whether file updates will actually be performed. Default is -- False. (I recommend running in a directory controlled by a version -- control system so you don't have to worry about this.) modifyDryRun :: MonadClean m => (Bool -> Bool) -> m () -- | If TestMode is turned on no import cleaning will occur after a split -- or cat. Default is False. modifyTestMode :: MonadClean m => (Bool -> Bool) -> m () module Language.Haskell.Modules.Imports -- | Clean up the imports of a source file. cleanImports :: MonadClean m => FilePath -> m ModuleResult tests :: Test module Language.Haskell.Modules.Merge -- | Merge the declarations from several modules into a single new one, -- updating the imports of the modules in the moduVerse to reflect the -- change. It *is* permissable to use one of the input modules as the -- output module. Note that circular imports can be created by this -- operation. mergeModules :: MonadClean m => [ModuleName] -> ModuleName -> m (Set ModuleResult) tests :: Test test1 :: Test test2 :: Test test3 :: Test module Language.Haskell.Modules.Split -- | Split each of a module's declarations into a new module. Update the -- imports of all the modules in the moduVerse to reflect the split. For -- example, if you have a module like -- --
-- module Start (a, b, (.+.)) where -- import -- a = 1 + a -- b = 2 -- c = 3 -- c' = 4 -- (.+.) = b + c ---- -- After running splitModule the Start module will be -- gone. The a and b symbols will be in new modules -- named Start.A and Start.B. Because they were not -- exported by Start, the c and c' symbols -- will both be in a new module named Start.Internal.C. And the -- .+. symbol will be in a module named -- Start.OtherSymbols. Note that this module needs to import new -- Start.A and Start.Internal.C modules. -- -- If we had imported and then re-exported a symbol in Start it would go -- into a module named Start.ReExported. Any instance -- declarations would go into Start.Instances. splitModule :: MonadClean m => ModuleName -> m () tests :: Test -- | This package provides three functions. The cleanImports -- function uses ghc's -ddump-minimal-imports flag to generate minimized -- and explicit imports and re-insert them into the module. -- -- The splitModule moves each declaration of a module into a -- separate new module, and may also create three additional modules: -- ReExported (for identifiers that were re-exported from other imports), -- Instances (for declarations that don't result in an identifier to -- export), and OtherSymbols (for declarations that can't be turned into -- a module name.) -- -- In addition to creating new modules, splitModule also scans the -- a set of modules (known as the moduVerse) and updates their imports to -- account for the new locations of the symbols. The moduVerse is stored -- in MonadClean's state, and is updated as modules are created and -- destroyed by splitModule and catModules. -- -- The catModules function is the inverse operation of -- splitModule, it merges two or more modules into a new or -- existing module, updating imports of the moduVerse elements as -- necessary. -- -- There are several features worth noting. The Params type in -- the state of MonadClean has a removeEmptyImports -- field, which is True by default. This determines whether imports that -- turn into empty lists are preserved or not - if your program needs -- instances from a such an import, you will either want to set this flag -- to False or (better) add an empty import list to the import. -- -- These are the important entry points: -- --
-- findPaths "Language" >>= runMonadClean . mapM cleanImports . toList ---- --
-- findModules "Language" >>= \ modules -> runMonadClean $ -- let mn = Language.Haskell.Exts.Syntax.ModuleName in -- modifyModuVerse (const modules) >> -- splitModule (mn "Language.Haskell.Modules.Common") >> -- mergeModules (map mn ["Language.Haskell.Modules.Common.WithCurrentDirectory", -- "Language.Haskell.Modules.Common.ModulePathBase"]) -- (mn "Language.Haskell.Modules.Common")) --module Language.Haskell.Modules -- | Clean up the imports of a source file. cleanImports :: MonadClean m => FilePath -> m ModuleResult -- | Split each of a module's declarations into a new module. Update the -- imports of all the modules in the moduVerse to reflect the split. For -- example, if you have a module like -- --
-- module Start (a, b, (.+.)) where -- import -- a = 1 + a -- b = 2 -- c = 3 -- c' = 4 -- (.+.) = b + c ---- -- After running splitModule the Start module will be -- gone. The a and b symbols will be in new modules -- named Start.A and Start.B. Because they were not -- exported by Start, the c and c' symbols -- will both be in a new module named Start.Internal.C. And the -- .+. symbol will be in a module named -- Start.OtherSymbols. Note that this module needs to import new -- Start.A and Start.Internal.C modules. -- -- If we had imported and then re-exported a symbol in Start it would go -- into a module named Start.ReExported. Any instance -- declarations would go into Start.Instances. splitModule :: MonadClean m => ModuleName -> m () -- | Merge the declarations from several modules into a single new one, -- updating the imports of the modules in the moduVerse to reflect the -- change. It *is* permissable to use one of the input modules as the -- output module. Note that circular imports can be created by this -- operation. mergeModules :: MonadClean m => [ModuleName] -> ModuleName -> m (Set ModuleResult) -- | Convenience function for building the moduVerse, searches for modules -- in a directory hierarchy. FIXME: This should be in MonadClean and use -- the value of sourceDirs to remove prefixes from the module paths. And -- then it should look at the module text to see what the module name -- really is. findModules :: FilePath -> IO (Set ModuleName) -- | Find the paths of all the files below the directory top. findPaths :: FilePath -> IO (Set FilePath)