ghc-mod 5.2.1.2 → 5.3.0.0
raw patch · 153 files changed
+6370/−3286 lines, 153 filesdep +bytestringdep +cabal-helperdep +cerealdep −Cabaldep −data-defaultdep −io-choicedep ~asyncdep ~containersdep ~deepseqbuild-type:Customsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring, cabal-helper, cereal, fclabels, ghc-prim
Dependencies removed: Cabal, data-default, io-choice
Dependency ranges changed: async, containers, deepseq, directory, filepath, ghc, ghc-syb-utils, haskell-src-exts, hlint, hspec, monad-control, monad-journal, mtl, old-time, pretty, process, syb, text, time, transformers, transformers-base
API changes (from Hackage documentation)
- Language.Haskell.GhcMod: GMEIOException :: IOException -> GhcModError
- Language.Haskell.GhcMod: cradlePkgDbStack :: Cradle -> [GhcPkgDb]
- Language.Haskell.GhcMod: type Expression = String
- Language.Haskell.GhcMod: type ModuleString = String
- Language.Haskell.GhcMod.Internal: (||>) :: GhcMonad m => m a -> m a -> m a
- Language.Haskell.GhcMod.Internal: CompilerOptions :: [GHCOption] -> [IncludeDir] -> [Package] -> CompilerOptions
- Language.Haskell.GhcMod.Internal: cabalAllBuildInfo :: PackageDescription -> [BuildInfo]
- Language.Haskell.GhcMod.Internal: cabalAllTargets :: PackageDescription -> IO ([String], [String], [String], [String])
- Language.Haskell.GhcMod.Internal: cabalDependPackages :: [BuildInfo] -> [PackageBaseName]
- Language.Haskell.GhcMod.Internal: cabalSourceDirs :: [BuildInfo] -> [IncludeDir]
- Language.Haskell.GhcMod.Internal: data CompilerOptions
- Language.Haskell.GhcMod.Internal: defaultState :: GhcModState
- Language.Haskell.GhcMod.Internal: depPackages :: CompilerOptions -> [Package]
- Language.Haskell.GhcMod.Internal: getCompilerOptions :: (IOish m, MonadError GhcModError m) => [GHCOption] -> Cradle -> PackageDescription -> m CompilerOptions
- Language.Haskell.GhcMod.Internal: getDynamicFlags :: IO DynFlags
- Language.Haskell.GhcMod.Internal: ghcOptions :: CompilerOptions -> [GHCOption]
- Language.Haskell.GhcMod.Internal: gmGhcSession :: GhcModEnv -> !(IORef HscEnv)
- Language.Haskell.GhcMod.Internal: goNext :: GhcMonad m => m a
- Language.Haskell.GhcMod.Internal: includeDirs :: CompilerOptions -> [IncludeDir]
- Language.Haskell.GhcMod.Internal: newGhcModEnv :: Options -> FilePath -> IO GhcModEnv
- Language.Haskell.GhcMod.Internal: parseCabalFile :: (IOish m, MonadError GhcModError m) => Cradle -> FilePath -> m PackageDescription
- Language.Haskell.GhcMod.Internal: runAnyOne :: GhcMonad m => [m a] -> m a
- Language.Haskell.GhcMod.Internal: setTargetFiles :: IOish m => [FilePath] -> GhcModT m ()
- Language.Haskell.GhcMod.Internal: type GhcModLog = ()
+ Language.Haskell.GhcMod: CabalProject :: ProjectType
+ Language.Haskell.GhcMod: Expression :: String -> Expression
+ Language.Haskell.GhcMod: GMECabalCompAssignment :: [(Either FilePath ModuleName, Set ChComponentName)] -> GhcModError
+ Language.Haskell.GhcMod: GMECabalComponent :: ChComponentName -> GhcModError
+ Language.Haskell.GhcMod: GMECabalStateFile :: GMConfigStateFileError -> GhcModError
+ Language.Haskell.GhcMod: PlainProject :: ProjectType
+ Language.Haskell.GhcMod: SandboxProject :: ProjectType
+ Language.Haskell.GhcMod: componentInfo :: IOish m => [String] -> GhcModT m String
+ Language.Haskell.GhcMod: cradleProjectType :: Cradle -> ProjectType
+ Language.Haskell.GhcMod: data GmLogLevel
+ Language.Haskell.GhcMod: data ModuleString
+ Language.Haskell.GhcMod: data ProjectType
+ Language.Haskell.GhcMod: decreaseLogLevel :: GmLogLevel -> GmLogLevel
+ Language.Haskell.GhcMod: dropSession :: IOish m => GhcModT m ()
+ Language.Haskell.GhcMod: getExpression :: Expression -> String
+ Language.Haskell.GhcMod: ghcPkgProgram :: Options -> FilePath
+ Language.Haskell.GhcMod: gmErrStr :: (MonadIO m, GmEnv m) => String -> m ()
+ Language.Haskell.GhcMod: gmErrStrLn :: (MonadIO m, GmEnv m) => String -> m ()
+ Language.Haskell.GhcMod: gmLog :: (MonadIO m, GmLog m, GmEnv m) => GmLogLevel -> String -> Doc -> m ()
+ Language.Haskell.GhcMod: gmPutStr :: (MonadIO m, GmEnv m) => String -> m ()
+ Language.Haskell.GhcMod: gmPutStrLn :: (MonadIO m, GmEnv m) => String -> m ()
+ Language.Haskell.GhcMod: gmSetLogLevel :: GmLog m => GmLogLevel -> m ()
+ Language.Haskell.GhcMod: gmUnsafeErrStrLn :: MonadIO m => Options -> String -> m ()
+ Language.Haskell.GhcMod: gmUnsafePutStrLn :: MonadIO m => Options -> String -> m ()
+ Language.Haskell.GhcMod: increaseLogLevel :: GmLogLevel -> GmLogLevel
+ Language.Haskell.GhcMod: linePrefix :: Options -> Maybe (String, String)
+ Language.Haskell.GhcMod: logLevel :: Options -> GmLogLevel
+ Language.Haskell.GhcMod: newtype Expression
+ Language.Haskell.GhcMod.Internal: GHandler :: (e -> m a) -> GHandler m a
+ Language.Haskell.GhcMod.Internal: GMCRaw :: GmComponentType
+ Language.Haskell.GhcMod.Internal: GMCResolved :: GmComponentType
+ Language.Haskell.GhcMod.Internal: GmComponent :: GmModuleGraph -> ChComponentName -> [GHCOption] -> [GHCOption] -> [GHCOption] -> [GHCOption] -> ChEntrypoint -> eps -> [FilePath] -> GmComponent eps
+ Language.Haskell.GhcMod.Internal: GmDebug :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmError :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmException :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmInfo :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmModuleGraph :: Map ModulePath (Set ModulePath) -> GmModuleGraph
+ Language.Haskell.GhcMod.Internal: GmPanic :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmSilent :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmVomit :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmWarning :: GmLogLevel
+ Language.Haskell.GhcMod.Internal: GmlT :: GhcModT m a -> GmlT m a
+ Language.Haskell.GhcMod.Internal: ModulePath :: ModuleName -> FilePath -> ModulePath
+ Language.Haskell.GhcMod.Internal: cabalResolvedComponents :: IOish m => GhcModT m (Map ChComponentName (GmComponent GMCResolved (Set ModulePath)))
+ Language.Haskell.GhcMod.Internal: class Monad m => GmEnv m where gmeAsk = gmeReader id gmeReader f = f `liftM` gmeAsk
+ Language.Haskell.GhcMod.Internal: class Monad m => GmLog m
+ Language.Haskell.GhcMod.Internal: class MonadIOC m => MonadIO m
+ Language.Haskell.GhcMod.Internal: data GHandler m a
+ Language.Haskell.GhcMod.Internal: data GhcModLog
+ Language.Haskell.GhcMod.Internal: data GmComponent (t :: GmComponentType) eps
+ Language.Haskell.GhcMod.Internal: data GmComponentType
+ Language.Haskell.GhcMod.Internal: data GmLogLevel
+ Language.Haskell.GhcMod.Internal: data GmModuleGraph
+ Language.Haskell.GhcMod.Internal: data ModulePath
+ Language.Haskell.GhcMod.Internal: gcatches :: (MonadIO m, ExceptionMonad m) => m a -> [GHandler m a] -> m a
+ Language.Haskell.GhcMod.Internal: gmOutput :: GhcModEnv -> GmOutput
+ Language.Haskell.GhcMod.Internal: gmSetLogLevel :: GmLog m => GmLogLevel -> m ()
+ Language.Haskell.GhcMod.Internal: gmcEntrypoints :: GmComponent eps -> eps
+ Language.Haskell.GhcMod.Internal: gmcGhcLangOpts :: GmComponent eps -> [GHCOption]
+ Language.Haskell.GhcMod.Internal: gmcGhcOpts :: GmComponent eps -> [GHCOption]
+ Language.Haskell.GhcMod.Internal: gmcGhcPkgOpts :: GmComponent eps -> [GHCOption]
+ Language.Haskell.GhcMod.Internal: gmcGhcSrcOpts :: GmComponent eps -> [GHCOption]
+ Language.Haskell.GhcMod.Internal: gmcHomeModuleGraph :: GmComponent eps -> GmModuleGraph
+ Language.Haskell.GhcMod.Internal: gmcName :: GmComponent eps -> ChComponentName
+ Language.Haskell.GhcMod.Internal: gmcRawEntrypoints :: GmComponent eps -> ChEntrypoint
+ Language.Haskell.GhcMod.Internal: gmcSourceDirs :: GmComponent eps -> [FilePath]
+ Language.Haskell.GhcMod.Internal: gmeAsk :: GmEnv m => m GhcModEnv
+ Language.Haskell.GhcMod.Internal: gmeLocal :: GmEnv m => (GhcModEnv -> GhcModEnv) -> m a -> m a
+ Language.Haskell.GhcMod.Internal: gmeReader :: GmEnv m => (GhcModEnv -> a) -> m a
+ Language.Haskell.GhcMod.Internal: gmgGraph :: GmModuleGraph -> Map ModulePath (Set ModulePath)
+ Language.Haskell.GhcMod.Internal: gmlClear :: GmLog m => m ()
+ Language.Haskell.GhcMod.Internal: gmlGetSession :: (MonadIO m, MonadBaseControl IO m) => GmlT m HscEnv
+ Language.Haskell.GhcMod.Internal: gmlHistory :: GmLog m => m GhcModLog
+ Language.Haskell.GhcMod.Internal: gmlJournal :: GmLog m => GhcModLog -> m ()
+ Language.Haskell.GhcMod.Internal: gmlSetSession :: (MonadIO m, MonadBaseControl IO m) => HscEnv -> GmlT m ()
+ Language.Haskell.GhcMod.Internal: liftIO :: MonadIO m => IO a -> m a
+ Language.Haskell.GhcMod.Internal: loadTargets :: IOish m => [String] -> GmlT m ()
+ Language.Haskell.GhcMod.Internal: mpModule :: ModulePath -> ModuleName
+ Language.Haskell.GhcMod.Internal: mpPath :: ModulePath -> FilePath
+ Language.Haskell.GhcMod.Internal: newtype GmlT m a
+ Language.Haskell.GhcMod.Internal: prepareCabalHelper :: (IOish m, GmEnv m, GmLog m) => m ()
+ Language.Haskell.GhcMod.Internal: runGmlT :: IOish m => [Either FilePath ModuleName] -> GmlT m a -> GhcModT m a
+ Language.Haskell.GhcMod.Internal: runGmlT' :: IOish m => [Either FilePath ModuleName] -> (DynFlags -> Ghc DynFlags) -> GmlT m a -> GhcModT m a
+ Language.Haskell.GhcMod.Internal: unGmlT :: GmlT m a -> GhcModT m a
- Language.Haskell.GhcMod: Cradle :: FilePath -> FilePath -> FilePath -> Maybe FilePath -> [GhcPkgDb] -> Cradle
+ Language.Haskell.GhcMod: Cradle :: ProjectType -> FilePath -> FilePath -> FilePath -> Maybe FilePath -> Cradle
- Language.Haskell.GhcMod: GMEProcess :: [String] -> GhcModError -> GhcModError
+ Language.Haskell.GhcMod: GMEProcess :: String -> [String] -> (Either (String, String, Int) GhcModError) -> GhcModError
- Language.Haskell.GhcMod: Options :: OutputStyle -> LineSeparator -> FilePath -> FilePath -> [GHCOption] -> Bool -> Bool -> Bool -> [String] -> Options
+ Language.Haskell.GhcMod: Options :: OutputStyle -> LineSeparator -> Maybe (String, String) -> GmLogLevel -> FilePath -> FilePath -> FilePath -> [GHCOption] -> Bool -> Bool -> Bool -> [String] -> Options
- Language.Haskell.GhcMod: browse :: IOish m => ModuleString -> GhcModT m String
+ Language.Haskell.GhcMod: browse :: IOish m => String -> GhcModT m String
- Language.Haskell.GhcMod: isOutdated :: SymbolDb -> IO Bool
+ Language.Haskell.GhcMod: isOutdated :: IOish m => SymbolDb -> GhcModT m Bool
- Language.Haskell.GhcMod: loadSymbolDb :: IOish m => GhcModT m SymbolDb
+ Language.Haskell.GhcMod: loadSymbolDb :: IOish m => FilePath -> GhcModT m SymbolDb
- Language.Haskell.GhcMod: modules :: IOish m => GhcModT m String
+ Language.Haskell.GhcMod: modules :: (IOish m, GmEnv m, GmState m, GmLog m) => m String
- Language.Haskell.GhcMod: withOptions :: IOish m => (Options -> Options) -> GhcModT m a -> GhcModT m a
+ Language.Haskell.GhcMod: withOptions :: GmEnv m => (Options -> Options) -> m a -> m a
- Language.Haskell.GhcMod.Internal: GhcModEnv :: !(IORef HscEnv) -> Options -> Cradle -> GhcModEnv
+ Language.Haskell.GhcMod.Internal: GhcModEnv :: Options -> Cradle -> GmOutput -> GhcModEnv
- Language.Haskell.GhcMod.Internal: cradle :: IOish m => GhcModT m Cradle
+ Language.Haskell.GhcMod.Internal: cradle :: GmEnv m => m Cradle
- Language.Haskell.GhcMod.Internal: didWorldChange :: World -> Cradle -> IO Bool
+ Language.Haskell.GhcMod.Internal: didWorldChange :: IOish m => World -> GhcModT m Bool
- Language.Haskell.GhcMod.Internal: getCompilerMode :: IOish m => GhcModT m CompilerMode
+ Language.Haskell.GhcMod.Internal: getCompilerMode :: GmState m => m CompilerMode
- Language.Haskell.GhcMod.Internal: getCurrentWorld :: Cradle -> IO World
+ Language.Haskell.GhcMod.Internal: getCurrentWorld :: IOish m => GhcModT m World
- Language.Haskell.GhcMod.Internal: options :: IOish m => GhcModT m Options
+ Language.Haskell.GhcMod.Internal: options :: GmEnv m => m Options
- Language.Haskell.GhcMod.Internal: runGhcModT' :: IOish m => GhcModEnv -> GhcModState -> GhcModT m a -> m (Either GhcModError (a, GhcModState), GhcModLog)
+ Language.Haskell.GhcMod.Internal: runGhcModT' :: IOish m => FilePath -> Options -> GhcModT m a -> m (Either GhcModError a, GhcModLog)
- Language.Haskell.GhcMod.Internal: setCompilerMode :: IOish m => CompilerMode -> GhcModT m ()
+ Language.Haskell.GhcMod.Internal: setCompilerMode :: GmState m => CompilerMode -> m ()
- Language.Haskell.GhcMod.Internal: withLogger :: IOish m => (DynFlags -> DynFlags) -> GhcModT m () -> GhcModT m (Either String String)
+ Language.Haskell.GhcMod.Internal: withLogger :: (GmGhc m, GmEnv m) => (DynFlags -> DynFlags) -> m a -> m (Either String (String, a))
- Language.Haskell.GhcMod.Internal: withOptions :: IOish m => (Options -> Options) -> GhcModT m a -> GhcModT m a
+ Language.Haskell.GhcMod.Internal: withOptions :: GmEnv m => (Options -> Options) -> m a -> m a
Files
- COPYING.AGPL3 +661/−0
- COPYING.BSD3 +29/−0
- ChangeLog +9/−0
- LICENSE +5/−28
- Language/Haskell/GhcMod.hs +21/−2
- Language/Haskell/GhcMod/Boot.hs +4/−2
- Language/Haskell/GhcMod/Browse.hs +47/−41
- Language/Haskell/GhcMod/Cabal16.hs +0/−45
- Language/Haskell/GhcMod/Cabal18.hs +0/−58
- Language/Haskell/GhcMod/Cabal21.hs +0/−73
- Language/Haskell/GhcMod/CabalApi.hs +0/−193
- Language/Haskell/GhcMod/CabalConfig.hs +0/−171
- Language/Haskell/GhcMod/CabalHelper.hs +228/−0
- Language/Haskell/GhcMod/Caching.hs +103/−0
- Language/Haskell/GhcMod/Caching/Types.hs +52/−0
- Language/Haskell/GhcMod/CaseSplit.hs +48/−30
- Language/Haskell/GhcMod/Check.hs +16/−17
- Language/Haskell/GhcMod/Convert.hs +27/−22
- Language/Haskell/GhcMod/Cradle.hs +47/−33
- Language/Haskell/GhcMod/Debug.hs +113/−25
- Language/Haskell/GhcMod/Doc.hs +8/−4
- Language/Haskell/GhcMod/DynFlags.hs +8/−27
- Language/Haskell/GhcMod/Error.hs +161/−37
- Language/Haskell/GhcMod/FillSig.hs +132/−67
- Language/Haskell/GhcMod/Find.hs +65/−82
- Language/Haskell/GhcMod/GHCApi.hs +0/−86
- Language/Haskell/GhcMod/GHCChoice.hs +0/−23
- Language/Haskell/GhcMod/Gap.hs +116/−54
- Language/Haskell/GhcMod/GhcPkg.hs +24/−37
- Language/Haskell/GhcMod/HomeModuleGraph.hs +263/−0
- Language/Haskell/GhcMod/Info.hs +46/−28
- Language/Haskell/GhcMod/Internal.hs +24/−21
- Language/Haskell/GhcMod/Logger.hs +73/−155
- Language/Haskell/GhcMod/Logging.hs +102/−0
- Language/Haskell/GhcMod/Modules.hs +17/−23
- Language/Haskell/GhcMod/Monad.hs +78/−436
- Language/Haskell/GhcMod/Monad/Types.hs +442/−0
- Language/Haskell/GhcMod/Output.hs +199/−0
- Language/Haskell/GhcMod/PathsAndFiles.hs +156/−47
- Language/Haskell/GhcMod/PkgDoc.hs +9/−8
- Language/Haskell/GhcMod/Pretty.hs +69/−0
- Language/Haskell/GhcMod/SrcUtils.hs +4/−22
- Language/Haskell/GhcMod/Target.hs +464/−38
- Language/Haskell/GhcMod/Types.hs +293/−40
- Language/Haskell/GhcMod/Utils.hs +131/−66
- Language/Haskell/GhcMod/World.hs +25/−62
- NotCPP/Declarations.hs +164/−0
- NotCPP/LookupValueName.hs +38/−0
- NotCPP/OrphanEvasion.hs +114/−0
- NotCPP/ScopeLookup.hs +65/−0
- NotCPP/Utils.hs +29/−0
- Setup.hs +197/−1
- SetupCompat.hs +198/−0
- Utils.hs +37/−0
- elisp/ghc-check.el +35/−26
- elisp/ghc-comp.el +1/−1
- elisp/ghc-doc.el +2/−2
- elisp/ghc-func.el +12/−2
- elisp/ghc-info.el +2/−2
- elisp/ghc-pkg.el +1/−1
- elisp/ghc-process.el +82/−37
- elisp/ghc.el +5/−6
- ghc-mod.cabal +109/−103
- src/GHCMod.hs +242/−231
- src/GHCModi.hs +41/−248
- src/Misc.hs +7/−114
- src/Utils.hs +0/−27
- test/BrowseSpec.hs +5/−4
- test/CabalApiSpec.hs +0/−77
- test/CheckSpec.hs +32/−14
- test/Dir.hs +8/−2
- test/HomeModuleGraphSpec.hs +178/−0
- test/InfoSpec.hs +19/−27
- test/LintSpec.hs +3/−3
- test/Main.hs +21/−4
- test/MonadSpec.hs +1/−23
- test/PathsAndFilesSpec.hs +13/−22
- test/TestUtils.hs +68/−22
- test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf +0/−4
- test/data/.cabal-sandbox/packages/00-index.tar binary
- test/data/Bar.hs +0/−5
- test/data/Baz.hs +0/−5
- test/data/Foo.hs +0/−9
- test/data/ForeignExport.hs +0/−10
- test/data/Info.hs +0/−8
- test/data/Main.hs +0/−3
- test/data/Mutual1.hs +0/−5
- test/data/Mutual2.hs +0/−3
- test/data/Unicode.hs +0/−4
- test/data/annotations/With.hs +6/−0
- test/data/cabal-flags/cabal-flags.cabal +1/−2
- test/data/cabal-preprocessors/Main.hs +4/−0
- test/data/cabal-preprocessors/Preprocessed.hsc +3/−0
- test/data/cabal-preprocessors/cabal-preprocessors.cabal +14/−0
- test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf +4/−0
- test/data/cabal-project/Baz.hs +5/−0
- test/data/cabal-project/Foo.hs +9/−0
- test/data/cabal-project/Info.hs +8/−0
- test/data/cabal-project/Main.hs +3/−0
- test/data/cabal-project/cabal.sandbox.config.in +25/−0
- test/data/cabal-project/cabalapi.cabal +67/−0
- test/data/cabal-project/subdir1/subdir2/dummy +1/−0
- test/data/cabal.sandbox.config.in +0/−25
- test/data/cabalapi.cabal +0/−67
- test/data/check-missing-warnings/DesugarWarnings.hs +5/−0
- test/data/check-test-subdir/check-test-subdir.cabal +1/−0
- test/data/custom-cradle/custom-cradle.cabal +12/−0
- test/data/custom-cradle/ghc-mod.package-db-stack +5/−0
- test/data/custom-cradle/package-db-a/.gitkeep +0/−0
- test/data/custom-cradle/package-db-b/.gitkeep +0/−0
- test/data/custom-cradle/package-db-c/.gitkeep +0/−0
- test/data/foreign-export/ForeignExport.hs +10/−0
- test/data/ghc-mod-check/Data/Foo.hs +0/−11
- test/data/ghc-mod-check/ghc-mod-check.cabal +2/−3
- test/data/ghc-mod-check/lib/Data/Foo.hs +11/−0
- test/data/hlint.hs +0/−5
- test/data/hlint/hlint.hs +5/−0
- test/data/home-module-graph/cpp/A.hs +4/−0
- test/data/home-module-graph/cpp/A1.hs +4/−0
- test/data/home-module-graph/cpp/A2.hs +1/−0
- test/data/home-module-graph/cpp/A3.hs +2/−0
- test/data/home-module-graph/cpp/B.hs +1/−0
- test/data/home-module-graph/cycle/A.hs +2/−0
- test/data/home-module-graph/cycle/B.hs +2/−0
- test/data/home-module-graph/errors/A.hs +4/−0
- test/data/home-module-graph/errors/A1.hs +4/−0
- test/data/home-module-graph/errors/A2.hs +1/−0
- test/data/home-module-graph/errors/A3.hs +2/−0
- test/data/home-module-graph/errors/B.hs +1/−0
- test/data/home-module-graph/indirect-update/A.hs +4/−0
- test/data/home-module-graph/indirect-update/A1.hs +2/−0
- test/data/home-module-graph/indirect-update/A2.hs +1/−0
- test/data/home-module-graph/indirect-update/A3.hs +2/−0
- test/data/home-module-graph/indirect-update/B.hs +1/−0
- test/data/home-module-graph/indirect-update/C.hs +1/−0
- test/data/home-module-graph/indirect/A.hs +4/−0
- test/data/home-module-graph/indirect/A1.hs +2/−0
- test/data/home-module-graph/indirect/A2.hs +2/−0
- test/data/home-module-graph/indirect/A3.hs +2/−0
- test/data/home-module-graph/indirect/B.hs +1/−0
- test/data/home-module-graph/indirect/C.hs +1/−0
- test/data/import-cycle/Mutual1.hs +5/−0
- test/data/import-cycle/Mutual2.hs +3/−0
- test/data/non-exported/Fib.hs +8/−0
- test/data/pattern-synonyms/pattern-synonyms.cabal +11/−10
- test/data/quasi-quotes/FooQ.hs +6/−0
- test/data/quasi-quotes/QuasiQuotes.hs +6/−0
- test/data/subdir1/subdir2/dummy +0/−1
- test/data/target/Cpp.hs +7/−0
- test/data/template-haskell/Bar.hs +5/−0
- test/data/template-haskell/Foo.hs +9/−0
- test/data/template-haskell/ImportsTH.hs +3/−0
- test/doctests.hs +7/−4
+ COPYING.AGPL3 view
@@ -0,0 +1,661 @@+ GNU AFFERO GENERAL PUBLIC LICENSE+ Version 3, 19 November 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU Affero General Public License is a free, copyleft license for+software and other kinds of works, specifically designed to ensure+cooperation with the community in the case of network server software.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+our General Public Licenses are intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ Developers that use our General Public Licenses protect your rights+with two steps: (1) assert copyright on the software, and (2) offer+you this License which gives you legal permission to copy, distribute+and/or modify the software.++ A secondary benefit of defending all users' freedom is that+improvements made in alternate versions of the program, if they+receive widespread use, become available for other developers to+incorporate. Many developers of free software are heartened and+encouraged by the resulting cooperation. However, in the case of+software used on network servers, this result may fail to come about.+The GNU General Public License permits making a modified version and+letting the public access it on a server without ever releasing its+source code to the public.++ The GNU Affero General Public License is designed specifically to+ensure that, in such cases, the modified source code becomes available+to the community. It requires the operator of a network server to+provide the source code of the modified version running there to the+users of that server. Therefore, public use of a modified version, on+a publicly accessible server, gives the public access to the source+code of the modified version.++ An older license, called the Affero General Public License and+published by Affero, was designed to accomplish similar goals. This is+a different license, not a version of the Affero GPL, but Affero has+released a new version of the Affero GPL which permits relicensing under+this license.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU Affero General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Remote Network Interaction; Use with the GNU General Public License.++ Notwithstanding any other provision of this License, if you modify the+Program, your modified version must prominently offer all users+interacting with it remotely through a computer network (if your version+supports such interaction) an opportunity to receive the Corresponding+Source of your version by providing access to the Corresponding Source+from a network server at no charge, through some standard or customary+means of facilitating copying of software. This Corresponding Source+shall include the Corresponding Source for any work covered by version 3+of the GNU General Public License that is incorporated pursuant to the+following paragraph.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the work with which it is combined will remain governed by version+3 of the GNU General Public License.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU Affero General Public License from time to time. Such new versions+will be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU Affero General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU Affero General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU Affero General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If your software can interact with users remotely through a computer+network, you should also make sure that it provides a way for users to+get its source. For example, if your program is a web application, its+interface could display a "Source" link that leads users to an archive+of the code. There are many ways you could offer source, and different+solutions will be better for different programs; see section 13 for the+specific requirements.++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU AGPL, see+<http://www.gnu.org/licenses/>.
+ COPYING.BSD3 view
@@ -0,0 +1,29 @@+Copyright (c) 2009, IIJ Innovation Institute Inc.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in+ the documentation and/or other materials provided with the+ distribution.+ * Neither the name of the copyright holders nor the names of its+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
ChangeLog view
@@ -1,3 +1,12 @@+2015-08-14 v5.3.0.0+ * Re-license majority of code under the AGPL-3+ * Add support for GHC 7.10 and Cabal 1.22+ * Remove `cabalDependPackages', `cabalAllTargets'+ * Merge #434, Fix finding sandbox config file and directory.+ * Merge #431, Re-add output line separator global option for expand command.+ * Merge #470, Support for overriding the package-db stack+ * Merge #486, Fix ineffective cache invalidation for `find`+ 2014-12-31 v5.2.1.2 * Merge #377, Fix `browse` erroneously thinking haskell2010 identifiers are operators
LICENSE view
@@ -1,29 +1,6 @@-Copyright (c) 2009, IIJ Innovation Institute Inc.-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions-are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.- * Redistributions in binary form must reproduce the above copyright- notice, this list of conditions and the following disclaimer in- the documentation and/or other materials provided with the- distribution.- * Neither the name of the copyright holders nor the names of its- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.+ghc-mod was originally licensed under the BSD3 but the primary license has been+changed to the AGPL3, files originally contributed under the BSD3 license remain+under this license and can generally be identified by the lack of a GPL header. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.+See the files COPYING.BSD3 and COPYING.AGPL3 in the source distribution for+copies of the two licenses.
Language/Haskell/GhcMod.hs view
@@ -3,15 +3,22 @@ module Language.Haskell.GhcMod ( -- * Cradle Cradle(..)+ , ProjectType(..) , findCradle -- * Options , Options(..) , LineSeparator(..) , OutputStyle(..) , defaultOptions+ -- * Logging+ , GmLogLevel+ , increaseLogLevel+ , decreaseLogLevel+ , gmSetLogLevel+ , gmLog -- * Types , ModuleString- , Expression+ , Expression(..) , GhcPkgDb , Symbol , SymbolDb@@ -22,12 +29,14 @@ -- * Monad utilities , runGhcModT , withOptions+ , dropSession -- * 'GhcMod' utilities , boot , browse , check , checkSyntax , debugInfo+ , componentInfo , expandTemplate , info , lint@@ -47,6 +56,13 @@ -- * SymbolDb , loadSymbolDb , isOutdated+ -- * Output+ , gmPutStr+ , gmErrStr+ , gmPutStrLn+ , gmErrStrLn+ , gmUnsafePutStrLn+ , gmUnsafeErrStrLn ) where import Language.Haskell.GhcMod.Boot@@ -61,7 +77,10 @@ import Language.Haskell.GhcMod.Info import Language.Haskell.GhcMod.Lang import Language.Haskell.GhcMod.Lint-import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Modules+import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.PkgDoc import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Target+import Language.Haskell.GhcMod.Output
Language/Haskell/GhcMod/Boot.hs view
@@ -1,6 +1,7 @@ module Language.Haskell.GhcMod.Boot where import Control.Applicative+import Prelude import Language.Haskell.GhcMod.Browse import Language.Haskell.GhcMod.Flag import Language.Haskell.GhcMod.Lang@@ -9,8 +10,9 @@ -- | Printing necessary information for front-end booting. boot :: IOish m => GhcModT m String-boot = concat <$> sequence [modules, languages, flags,- concat <$> mapM browse preBrowsedModules]+boot = concat <$> sequence ms+ where+ ms = [modules, languages, flags, concat <$> mapM browse preBrowsedModules] preBrowsedModules :: [String] preBrowsedModules = [
Language/Haskell/GhcMod/Browse.hs view
@@ -2,54 +2,57 @@ browse ) where -import Control.Applicative ((<$>))+import Control.Applicative import Control.Exception (SomeException(..))-import Data.Char (isAlpha)-import Data.List (sort)-import Data.Maybe (catMaybes)-import Exception (ghandle)-import FastString (mkFastString)-import GHC (GhcException(CmdLineError), ModuleInfo, Name, TyThing, DynFlags, Type, TyCon)+import Data.Char+import Data.List+import Data.Maybe+import FastString+import GHC import qualified GHC as G import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Doc (showPage, styleUnqualified)-import Language.Haskell.GhcMod.Gap-import Language.Haskell.GhcMod.Monad (GhcModT, options)-import Language.Haskell.GhcMod.Target (setTargetFiles)+import Language.Haskell.GhcMod.Gap as Gap+import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Name (getOccString)-import Outputable (ppr, Outputable)+import Outputable import TyCon (isAlgTyCon) import Type (dropForAlls, splitFunTy_maybe, mkFunTy, isPredTy)+import Exception (ExceptionMonad, ghandle)+import Prelude ---------------------------------------------------------------- -- | Getting functions, classes, etc from a module. -- If 'detailed' is 'True', their types are also obtained. -- If 'operators' is 'True', operators are also returned.-browse :: IOish m- => ModuleString -- ^ A module name. (e.g. \"Data.List\")+browse :: forall m. IOish m+ => String -- ^ A module name. (e.g. \"Data.List\", "base:Prelude") -> GhcModT m String-browse pkgmdl = convert' . sort =<< (listExports =<< getModule)+browse pkgmdl = do+ convert' . sort =<< go where- (mpkg,mdl) = splitPkgMdl pkgmdl+ -- TODO: Add API to Gm.Target to check if module is home module without+ -- bringing up a GHC session as well then this can be made a lot cleaner+ go = ghandle (\(SomeException _) -> return []) $ do+ goPkgModule `G.gcatch` (\(SomeException _) -> goHomeModule)++ goPkgModule = do+ opt <- options+ runGmPkgGhc $+ processExports opt =<< tryModuleInfo =<< G.findModule mdlname mpkgid++ goHomeModule = runGmlT [Right mdlname] $ do+ opt <- options+ processExports opt =<< tryModuleInfo =<< G.findModule mdlname Nothing++ tryModuleInfo m = fromJust <$> G.getModuleInfo m++ (mpkg, mdl) = splitPkgMdl pkgmdl mdlname = G.mkModuleName mdl mpkgid = mkFastString <$> mpkg- listExports Nothing = return []- listExports (Just mdinfo) = processExports mdinfo- -- findModule works only for package modules, moreover,- -- you cannot load a package module. On the other hand,- -- to browse a local module you need to load it first.- -- If CmdLineError is signalled, we assume the user- -- tried browsing a local module.- getModule = browsePackageModule `G.gcatch` fallback `G.gcatch` handler- browsePackageModule = G.findModule mdlname mpkgid >>= G.getModuleInfo- browseLocalModule = ghandle handler $ do- setTargetFiles [mdl]- G.findModule mdlname Nothing >>= G.getModuleInfo- fallback (CmdLineError _) = browseLocalModule- fallback _ = return Nothing- handler (SomeException _) = return Nothing+ -- | -- -- >>> splitPkgMdl "base:Prelude"@@ -57,9 +60,10 @@ -- >>> splitPkgMdl "Prelude" -- (Nothing,"Prelude") splitPkgMdl :: String -> (Maybe String,String)-splitPkgMdl pkgmdl = case break (==':') pkgmdl of- (mdl,"") -> (Nothing,mdl)- (pkg,_:mdl) -> (Just pkg,mdl)+splitPkgMdl pkgmdl =+ case break (==':') pkgmdl of+ (mdl, "") -> (Nothing, mdl)+ (pkg, _:mdl) -> (Just pkg, mdl) -- Haskell 2010: -- small -> ascSmall | uniSmall | _@@ -71,22 +75,23 @@ isNotOp (h:_) = isAlpha h || (h == '_') isNotOp _ = error "isNotOp" -processExports :: IOish m => ModuleInfo -> GhcModT m [String]-processExports minfo = do- opt <- options+processExports :: (G.GhcMonad m, MonadIO m, ExceptionMonad m)+ => Options -> ModuleInfo -> m [String]+processExports opt minfo = do let removeOps | operators opt = id | otherwise = filter (isNotOp . getOccString) mapM (showExport opt minfo) $ removeOps $ G.modInfoExports minfo -showExport :: IOish m => Options -> ModuleInfo -> Name -> GhcModT m String+showExport :: forall m. (G.GhcMonad m, MonadIO m, ExceptionMonad m)+ => Options -> ModuleInfo -> Name -> m String showExport opt minfo e = do mtype' <- mtype return $ concat $ catMaybes [mqualified, Just $ formatOp $ getOccString e, mtype'] where mqualified = (G.moduleNameString (G.moduleName $ G.nameModule e) ++ ".") `justIf` qualified opt- mtype :: IOish m => GhcModT m (Maybe String)+ mtype :: m (Maybe String) mtype | detailed opt = do tyInfo <- G.modInfoLookupName minfo e@@ -101,8 +106,9 @@ | null nm = error "formatOp" | isNotOp nm = nm | otherwise = "(" ++ nm ++ ")"- inOtherModule :: IOish m => Name -> GhcModT m (Maybe TyThing)- inOtherModule nm = G.getModuleInfo (G.nameModule nm) >> G.lookupGlobalName nm+ inOtherModule :: Name -> m (Maybe TyThing)+ inOtherModule nm = do+ G.getModuleInfo (G.nameModule nm) >> G.lookupGlobalName nm justIf :: a -> Bool -> Maybe a justIf x True = Just x justIf _ False = Nothing@@ -127,7 +133,7 @@ && not (G.isClassTyCon typ) = Just "data" | G.isNewTyCon typ = Just "newtype" | G.isClassTyCon typ = Just "class"- | G.isSynTyCon typ = Just "type"+ | Gap.isSynTyCon typ = Just "type" | otherwise = Nothing removeForAlls :: Type -> Type
− Language/Haskell/GhcMod/Cabal16.hs
@@ -1,45 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}----- | ComponentLocalBuildInfo for Cabal <= 1.16-module Language.Haskell.GhcMod.Cabal16 (- ComponentLocalBuildInfo- , componentPackageDeps- ) where--import Distribution.Package (InstalledPackageId, PackageIdentifier)---- From Cabal <= 1.16-data ComponentLocalBuildInfo = ComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageIdentifier)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/Cabal18.hs
@@ -1,58 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}---- | ComponentLocalBuildInfo for Cabal >= 1.18-module Language.Haskell.GhcMod.Cabal18 (- ComponentLocalBuildInfo- , componentPackageDeps- , componentLibraries- ) where--import Distribution.Package (InstalledPackageId, PackageId)--data LibraryName = LibraryName String- deriving (Read, Show)--data ComponentLocalBuildInfo- = LibComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)],- componentLibraries :: [LibraryName]- }- | ExeComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | TestComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | BenchComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/Cabal21.hs
@@ -1,73 +0,0 @@--- Copyright : Isaac Jones 2003-2004-{- All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are-met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of Isaac Jones nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}---- | ComponentLocalBuildInfo for Cabal >= 1.21-module Language.Haskell.GhcMod.Cabal21 (- ComponentLocalBuildInfo- , PackageIdentifier(..)- , PackageName(..)- , componentPackageDeps- , componentLibraries- ) where--import Distribution.Package (InstalledPackageId)-import Data.Version (Version)--data LibraryName = LibraryName String- deriving (Read, Show)--newtype PackageName = PackageName { unPackageName :: String }- deriving (Read, Show)--data PackageIdentifier- = PackageIdentifier {- pkgName :: PackageName,- pkgVersion :: Version- }- deriving (Read, Show)--type PackageId = PackageIdentifier--data ComponentLocalBuildInfo- = LibComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)],- componentLibraries :: [LibraryName]- }- | ExeComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | TestComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- | BenchComponentLocalBuildInfo {- componentPackageDeps :: [(InstalledPackageId, PackageId)]- }- deriving (Read, Show)
− Language/Haskell/GhcMod/CabalApi.hs
@@ -1,193 +0,0 @@-{-# LANGUAGE OverloadedStrings, CPP #-}--module Language.Haskell.GhcMod.CabalApi (- getCompilerOptions- , parseCabalFile- , cabalAllBuildInfo- , cabalDependPackages- , cabalSourceDirs- , cabalAllTargets- , cabalConfigDependencies- ) where--import Language.Haskell.GhcMod.CabalConfig-import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.Gap (benchmarkBuildInfo, benchmarkTargets,- toModuleString)-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Types--import MonadUtils (liftIO)-import Control.Applicative ((<$>))-import qualified Control.Exception as E-import Control.Monad (filterM)-import Data.Maybe (maybeToList)-import Data.Set (fromList, toList)-import Distribution.Package (Dependency(Dependency)- , PackageName(PackageName))-import qualified Distribution.Package as C-import Distribution.PackageDescription (PackageDescription, BuildInfo, TestSuite, TestSuiteInterface(..), Executable)-import qualified Distribution.PackageDescription as P-import Distribution.PackageDescription.Configuration (finalizePackageDescription)-import Distribution.PackageDescription.Parse (readPackageDescription)-import Distribution.Simple.Compiler (CompilerId(..), CompilerFlavor(..))-import Distribution.Simple.Program as C (ghcProgram)-import Distribution.Simple.Program.Types (programName, programFindVersion)-import Distribution.System (buildPlatform)-import Distribution.Text (display)-import Distribution.Verbosity (silent)-import Distribution.Version (Version)-import System.Directory (doesFileExist)-import System.FilePath ((</>))--------------------------------------------------------------------- | Getting necessary 'CompilerOptions' from three information sources.-getCompilerOptions :: (IOish m, MonadError GhcModError m)- => [GHCOption]- -> Cradle- -> PackageDescription- -> m CompilerOptions-getCompilerOptions ghcopts cradle pkgDesc = do- gopts <- liftIO $ getGHCOptions ghcopts cradle rdir $ head buildInfos- depPkgs <- cabalConfigDependencies cradle (C.packageId pkgDesc)- return $ CompilerOptions gopts idirs depPkgs- where- wdir = cradleCurrentDir cradle- rdir = cradleRootDir cradle- buildInfos = cabalAllBuildInfo pkgDesc- idirs = includeDirectories rdir wdir $ cabalSourceDirs buildInfos--------------------------------------------------------------------- Include directories for modules--cabalBuildDirs :: [FilePath]-cabalBuildDirs = ["dist/build", "dist/build/autogen"]--includeDirectories :: FilePath -> FilePath -> [FilePath] -> [FilePath]-includeDirectories cdir wdir dirs = uniqueAndSort (extdirs ++ [cdir,wdir])- where- extdirs = map expand $ dirs ++ cabalBuildDirs- expand "." = cdir- expand subdir = cdir </> subdir---------------------------------------------------------------------- | Parse a cabal file and return a 'PackageDescription'.-parseCabalFile :: (IOish m, MonadError GhcModError m)- => Cradle- -> FilePath- -> m PackageDescription-parseCabalFile cradle file = do- cid <- liftIO getGHCId- epgd <- liftIO $ readPackageDescription silent file- flags <- cabalConfigFlags cradle- case toPkgDesc cid flags epgd of- Left deps -> fail $ show deps ++ " are not installed"- Right (pd,_) -> if nullPkg pd- then fail $ file ++ " is broken"- else return pd- where- toPkgDesc cid flags =- finalizePackageDescription flags (const True) buildPlatform cid []- nullPkg pd = name == ""- where- PackageName name = C.pkgName (P.package pd)--------------------------------------------------------------------getGHCOptions :: [GHCOption] -> Cradle -> FilePath -> BuildInfo -> IO [GHCOption]-getGHCOptions ghcopts cradle rdir binfo = do- cabalCpp <- cabalCppOptions rdir- let cpps = map ("-optP" ++) $ P.cppOptions binfo ++ cabalCpp- return $ ghcopts ++ pkgDb ++ exts ++ [lang] ++ libs ++ libDirs ++ cpps- where- pkgDb = ghcDbStackOpts $ cradlePkgDbStack cradle- lang = maybe "-XHaskell98" (("-X" ++) . display) $ P.defaultLanguage binfo- libDirs = map ("-L" ++) $ P.extraLibDirs binfo- exts = map (("-X" ++) . display) $ P.usedExtensions binfo- libs = map ("-l" ++) $ P.extraLibs binfo--cabalCppOptions :: FilePath -> IO [String]-cabalCppOptions dir = do- exist <- doesFileExist cabalMacro- return $ if exist then- ["-include", cabalMacro]- else- []- where- cabalMacro = dir </> "dist/build/autogen/cabal_macros.h"---------------------------------------------------------------------- | Extracting all 'BuildInfo' for libraries, executables, and tests.-cabalAllBuildInfo :: PackageDescription -> [BuildInfo]-cabalAllBuildInfo pd = libBI ++ execBI ++ testBI ++ benchBI- where- libBI = map P.libBuildInfo $ maybeToList $ P.library pd- execBI = map P.buildInfo $ P.executables pd- testBI = map P.testBuildInfo $ P.testSuites pd- benchBI = benchmarkBuildInfo pd---------------------------------------------------------------------- | Extracting package names of dependency.-cabalDependPackages :: [BuildInfo] -> [PackageBaseName]-cabalDependPackages bis = uniqueAndSort pkgs- where- pkgs = map getDependencyPackageName $ concatMap P.targetBuildDepends bis- getDependencyPackageName (Dependency (PackageName nm) _) = nm---------------------------------------------------------------------- | Extracting include directories for modules.-cabalSourceDirs :: [BuildInfo] -> [IncludeDir]-cabalSourceDirs bis = uniqueAndSort $ concatMap P.hsSourceDirs bis--------------------------------------------------------------------uniqueAndSort :: [String] -> [String]-uniqueAndSort = toList . fromList--------------------------------------------------------------------getGHCId :: IO CompilerId-getGHCId = CompilerId GHC <$> getGHC--getGHC :: IO Version-getGHC = do- mv <- programFindVersion C.ghcProgram silent (programName C.ghcProgram)- case mv of- -- TODO: MonadError it up- Nothing -> E.throwIO $ userError "ghc not found"- Just v -> return v---------------------------------------------------------------------- | Extracting all 'Module' 'FilePath's for libraries, executables,--- tests and benchmarks.-cabalAllTargets :: PackageDescription -> IO ([String],[String],[String],[String])-cabalAllTargets pd = do- exeTargets <- mapM getExecutableTarget $ P.executables pd- testTargets <- mapM getTestTarget $ P.testSuites pd- return (libTargets,concat exeTargets,concat testTargets,benchTargets)- where- lib = case P.library pd of- Nothing -> []- Just l -> P.libModules l-- libTargets = map toModuleString lib- benchTargets = benchmarkTargets pd-- getTestTarget :: TestSuite -> IO [String]- getTestTarget ts =- case P.testInterface ts of- (TestSuiteExeV10 _ filePath) -> do- let maybeTests = [p </> e | p <- P.hsSourceDirs $ P.testBuildInfo ts, e <- [filePath]]- liftIO $ filterM doesFileExist maybeTests- (TestSuiteLibV09 _ moduleName) -> return [toModuleString moduleName]- (TestSuiteUnsupported _) -> return []-- getExecutableTarget :: Executable -> IO [String]- getExecutableTarget exe = do- let maybeExes = [p </> e | p <- P.hsSourceDirs $ P.buildInfo exe, e <- [P.modulePath exe]]- liftIO $ filterM doesFileExist maybeExes
− Language/Haskell/GhcMod/CabalConfig.hs
@@ -1,171 +0,0 @@-{-# LANGUAGE RecordWildCards, CPP #-}---- | This module facilitates extracting information from Cabal's on-disk--- 'LocalBuildInfo' (@dist/setup-config@).-module Language.Haskell.GhcMod.CabalConfig (- CabalConfig- , cabalConfigDependencies- , cabalConfigFlags- ) where--import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.PathsAndFiles-import Language.Haskell.GhcMod.Read-import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Utils-import Language.Haskell.GhcMod.World--import qualified Language.Haskell.GhcMod.Cabal16 as C16-import qualified Language.Haskell.GhcMod.Cabal18 as C18-import qualified Language.Haskell.GhcMod.Cabal21 as C21--#ifndef MIN_VERSION_mtl-#define MIN_VERSION_mtl(x,y,z) 1-#endif--import Control.Applicative ((<$>))-import Control.Monad (void, mplus, when)-#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except ()-#else-import Control.Monad.Error ()-#endif-import Data.List (find,tails,isPrefixOf,isInfixOf,nub,stripPrefix)-import Distribution.Package (InstalledPackageId(..)- , PackageIdentifier(..)- , PackageName(..))-import Distribution.PackageDescription (FlagAssignment)-import Distribution.Simple.LocalBuildInfo (ComponentName)-import MonadUtils (liftIO)---------------------------------------------------------------------- | 'Show'ed cabal 'LocalBuildInfo' string-type CabalConfig = String---- | Get contents of the file containing 'LocalBuildInfo' data. If it doesn't--- exist run @cabal configure@ i.e. configure with default options like @cabal--- build@ would do.-getConfig :: (IOish m, MonadError GhcModError m)- => Cradle- -> m CabalConfig-getConfig cradle = do- outOfDate <- liftIO $ isSetupConfigOutOfDate cradle- when outOfDate configure- liftIO (readFile file) `tryFix` \_ ->- configure `modifyError'` GMECabalConfigure- where- file = setupConfigFile cradle- prjDir = cradleRootDir cradle-- configure :: (IOish m, MonadError GhcModError m) => m ()- configure = withDirectory_ prjDir $ void $ readProcess' "cabal" ["configure"]---- | Get list of 'Package's needed by all components of the current package-cabalConfigDependencies :: (IOish m, MonadError GhcModError m)- => Cradle- -> PackageIdentifier- -> m [Package]-cabalConfigDependencies cradle thisPkg =- configDependencies thisPkg <$> getConfig cradle---- | Extract list of depencenies for all components from 'CabalConfig'-configDependencies :: PackageIdentifier -> CabalConfig -> [Package]-configDependencies thisPkg config = map fromInstalledPackageId deps- where- deps :: [InstalledPackageId]- deps = case deps21 `mplus` deps18 `mplus` deps16 of- Right ps -> ps- Left msg -> error msg-- -- True if this dependency is an internal one (depends on the library- -- defined in the same package).- internal pkgid = pkgid == thisPkg-- -- Cabal >= 1.21- deps21 :: Either String [InstalledPackageId]- deps21 =- map fst- <$> filterInternal21- <$> (readEither =<< extractField config "componentsConfigs")-- filterInternal21- :: [(ComponentName, C21.ComponentLocalBuildInfo, [ComponentName])]- -> [(InstalledPackageId, C21.PackageIdentifier)]-- filterInternal21 ccfg = [ (ipkgid, pkgid)- | (_,clbi,_) <- ccfg- , (ipkgid, pkgid) <- C21.componentPackageDeps clbi- , not (internal . packageIdentifierFrom21 $ pkgid) ]-- packageIdentifierFrom21 :: C21.PackageIdentifier -> PackageIdentifier- packageIdentifierFrom21 (C21.PackageIdentifier (C21.PackageName myName) myVersion) =- PackageIdentifier (PackageName myName) myVersion-- -- Cabal >= 1.18 && < 1.21- deps18 :: Either String [InstalledPackageId]- deps18 =- map fst- <$> filterInternal- <$> (readEither =<< extractField config "componentsConfigs")-- filterInternal- :: [(ComponentName, C18.ComponentLocalBuildInfo, [ComponentName])]- -> [(InstalledPackageId, PackageIdentifier)]-- filterInternal ccfg = [ (ipkgid, pkgid)- | (_,clbi,_) <- ccfg- , (ipkgid, pkgid) <- C18.componentPackageDeps clbi- , not (internal pkgid) ]-- -- Cabal 1.16 and below- deps16 :: Either String [InstalledPackageId]- deps16 = map fst <$> filter (not . internal . snd) . nub <$> do- cbi <- concat <$> sequence [ extract "executableConfigs"- , extract "testSuiteConfigs"- , extract "benchmarkConfigs" ]- :: Either String [(String, C16.ComponentLocalBuildInfo)]-- return $ maybe [] C16.componentPackageDeps libraryConfig- ++ concatMap (C16.componentPackageDeps . snd) cbi- where- libraryConfig :: Maybe C16.ComponentLocalBuildInfo- libraryConfig = do- field <- find ("libraryConfig" `isPrefixOf`) (tails config)- clbi <- stripPrefix " = " field- if "Nothing" `isPrefixOf` clbi- then Nothing- else case readMaybe =<< stripPrefix "Just " clbi of- Just x -> x- Nothing -> error $ "reading libraryConfig failed\n" ++ show (stripPrefix "Just " clbi)-- extract :: String -> Either String [(String, C16.ComponentLocalBuildInfo)]- extract field = readConfigs field <$> extractField config field-- readConfigs :: String -> String -> [(String, C16.ComponentLocalBuildInfo)]- readConfigs f s = case readEither s of- Right x -> x- Left msg -> error $ "reading config " ++ f ++ " failed ("++msg++")"---- | Get the flag assignment from the local build info of the given cradle-cabalConfigFlags :: (IOish m, MonadError GhcModError m)- => Cradle- -> m FlagAssignment-cabalConfigFlags cradle = do- config <- getConfig cradle- case configFlags config of- Right x -> return x- Left msg -> throwError (GMECabalFlags (GMEString msg))---- | Extract the cabal flags from the 'CabalConfig'-configFlags :: CabalConfig -> Either String FlagAssignment-configFlags config = readEither =<< flip extractField "configConfigurationsFlags" =<< extractField config "configFlags"---- | Find @field@ in 'CabalConfig'. Returns 'Left' containing a user readable--- error message with lots of context on failure.-extractField :: CabalConfig -> String -> Either String String-extractField config field =- case extractParens <$> find (field `isPrefixOf`) (tails config) of- Just f -> Right f- Nothing -> Left $ "extractField: failed extracting "++field++" from input, input contained `"++field++"'? " ++ show (field `isInfixOf` config)
+ Language/Haskell/GhcMod/CabalHelper.hs view
@@ -0,0 +1,228 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP #-}+module Language.Haskell.GhcMod.CabalHelper+#ifndef SPEC+ ( getComponents+ , getGhcMergedPkgOptions+ , getCabalPackageDbStack+ , getCustomPkgDbStack+ , prepareCabalHelper+ )+#endif+ where++import Control.Applicative+import Control.Monad+import Control.Category ((.))+import Data.Maybe+import Data.Monoid+import Data.Serialize (Serialize)+import Data.Traversable+import Distribution.Helper+import qualified Language.Haskell.GhcMod.Types as T+import Language.Haskell.GhcMod.Types hiding (ghcProgram, ghcPkgProgram,+ cabalProgram)+import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.Utils+import Language.Haskell.GhcMod.PathsAndFiles+import Language.Haskell.GhcMod.Logging+import Language.Haskell.GhcMod.Output+import System.FilePath+import Prelude hiding ((.))++import Paths_ghc_mod as GhcMod++-- | Only package related GHC options, sufficient for things that don't need to+-- access home modules+getGhcMergedPkgOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m)+ => m [GHCOption]+getGhcMergedPkgOptions = chCached Cached {+ cacheLens = Just (lGmcMergedPkgOptions . lGmCaches),+ cacheFile = mergedPkgOptsCacheFile,+ cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do+ readProc <- gmReadProcess+ opts <- withCabal $ runQuery'' readProc progs rootdir distdir $+ ghcMergedPkgOptions+ return ([setupConfigPath], opts)+ }++getCabalPackageDbStack :: (IOish m, GmEnv m, GmState m, GmLog m) => m [GhcPkgDb]+getCabalPackageDbStack = chCached Cached {+ cacheLens = Just (lGmcPackageDbStack . lGmCaches),+ cacheFile = pkgDbStackCacheFile,+ cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do+ readProc <- gmReadProcess+ dbs <- withCabal $ map chPkgToGhcPkg <$> runQuery'' readProc progs rootdir distdir packageDbStack+ return ([setupConfigPath, sandboxConfigFile], dbs)+ }++chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb+chPkgToGhcPkg ChPkgGlobal = GlobalDb+chPkgToGhcPkg ChPkgUser = UserDb+chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f++-- | Primary interface to cabal-helper and intended single entrypoint to+-- constructing 'GmComponent's+--+-- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by+-- 'resolveGmComponents'.+getComponents :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m)+ => m [GmComponent 'GMCRaw ChEntrypoint]+getComponents = chCached Cached {+ cacheLens = Just (lGmcComponents . lGmCaches),+ cacheFile = cabalHelperCacheFile,+ cachedAction = \ _tcf (progs, rootdir, distdir, _vers) _ma -> do+ readProc <- gmReadProcess+ runQuery'' readProc progs rootdir distdir $ do+ q <- join7+ <$> ghcOptions+ <*> ghcPkgOptions+ <*> ghcSrcOptions+ <*> ghcLangOptions+ <*> entrypoints+ <*> entrypoints+ <*> sourceDirs+ let cs = flip map q $ curry8 (GmComponent mempty)+ return ([setupConfigPath], cs)+ }+ where+ curry8 fn (a, (b, (c, (d, (e, (f, (g, h))))))) = fn a b c d e f g h++ join7 a b c d e f = join' a . join' b . join' c . join' d . join' e . join' f+ join' :: Eq a => [(a,b)] -> [(a,c)] -> [(a,(b,c))]+ join' lb lc = [ (a, (b, c))+ | (a, b) <- lb+ , (a', c) <- lc+ , a == a'+ ]++prepareCabalHelper :: (IOish m, GmEnv m, GmLog m) => m ()+prepareCabalHelper = do+ crdl <- cradle+ let projdir = cradleRootDir crdl+ distdir = projdir </> "dist"+ readProc <- gmReadProcess+ when (cradleProjectType crdl == CabalProject) $+ withCabal $ liftIO $ prepare readProc projdir distdir++parseCustomPackageDb :: String -> [GhcPkgDb]+parseCustomPackageDb src = map parsePkgDb $ filter (not . null) $ lines src+ where+ parsePkgDb "global" = GlobalDb+ parsePkgDb "user" = UserDb+ parsePkgDb s = PackageDb s++getCustomPkgDbStack :: (IOish m, GmEnv m) => m (Maybe [GhcPkgDb])+getCustomPkgDbStack = do+ mCusPkgDbFile <- liftIO . (traverse readFile <=< findCustomPackageDbFile) . cradleRootDir =<< cradle+ return $ parseCustomPackageDb <$> mCusPkgDbFile++withCabal :: (IOish m, GmEnv m, GmLog m) => m a -> m a+withCabal action = do+ crdl <- cradle+ opts <- options+ readProc <- gmReadProcess++ let projdir = cradleRootDir crdl+ distdir = projdir </> "dist"++ mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl+ mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl)++ mCusPkgDbStack <- getCustomPkgDbStack++ pkgDbStackOutOfSync <-+ case mCusPkgDbStack of+ Just cusPkgDbStack -> do+ pkgDb <- runQuery'' readProc (helperProgs opts) projdir distdir $+ map chPkgToGhcPkg <$> packageDbStack+ return $ pkgDb /= cusPkgDbStack++ Nothing -> return False++ cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack++ --TODO: also invalidate when sandboxConfig file changed++ when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $+ gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project."+ when pkgDbStackOutOfSync $+ gmLog GmDebug "" $ strDoc $ "package-db stack out of sync with ghc-mod.package-db-stack, reconfiguring Cabal project."++ when (isSetupConfigOutOfDate mCabalFile mCabalConfig || pkgDbStackOutOfSync) $+ withDirectory_ (cradleRootDir crdl) $ do+ let progOpts =+ [ "--with-ghc=" ++ T.ghcProgram opts ]+ -- Only pass ghc-pkg if it was actually set otherwise we+ -- might break cabal's guessing logic+ ++ if T.ghcPkgProgram opts /= T.ghcPkgProgram defaultOptions+ then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram opts ]+ else []+ ++ map pkgDbArg cusPkgStack+ liftIO $ void $ readProc (T.cabalProgram opts) ("configure":progOpts) ""+ gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files"+ liftIO $ writeAutogenFiles readProc projdir distdir+ action++pkgDbArg :: GhcPkgDb -> String+pkgDbArg GlobalDb = "--package-db=global"+pkgDbArg UserDb = "--package-db=user"+pkgDbArg (PackageDb p) = "--package-db=" ++ p++-- * Neither file exists -> should return False:+-- @Nothing < Nothing = False@+-- (since we don't need to @cabal configure@ when no cabal file exists.)+--+-- * Cabal file doesn't exist (unlikely case) -> should return False+-- @Just cc < Nothing = False@+-- TODO: should we delete dist/setup-config?+--+-- * dist/setup-config doesn't exist yet -> should return True:+-- @Nothing < Just cf = True@+--+-- * Both files exist+-- @Just cc < Just cf = cc < cf = cc `olderThan` cf@+isSetupConfigOutOfDate :: Maybe TimedFile -> Maybe TimedFile -> Bool+isSetupConfigOutOfDate worldCabalFile worldCabalConfig = do+ worldCabalConfig < worldCabalFile+++helperProgs :: Options -> Programs+helperProgs opts = Programs {+ cabalProgram = T.cabalProgram opts,+ ghcProgram = T.ghcProgram opts,+ ghcPkgProgram = T.ghcPkgProgram opts+ }++chCached :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m, Serialize a)+ => Cached m GhcModState ChCacheData a -> m a+chCached c = do+ root <- cradleRootDir <$> cradle+ d <- cacheInputData root+ withCabal $ cached root c d+ where+ cacheInputData root = do+ opt <- options+ return $ ( helperProgs opt+ , root+ , root </> "dist"+ , (gmVer, chVer)+ )++ gmVer = GhcMod.version+ chVer = VERSION_cabal_helper
+ Language/Haskell/GhcMod/Caching.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE OverloadedStrings #-}+module Language.Haskell.GhcMod.Caching (+ module Language.Haskell.GhcMod.Caching+ , module Language.Haskell.GhcMod.Caching.Types+ ) where++import Control.Arrow (first)+import Control.Monad+import Control.Monad.Trans.Maybe+import Data.Maybe+import Data.Serialize (Serialize, encode, decode)+import Data.Version+import Data.Label+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BS8+import System.FilePath+import Utils (TimedFile(..), timeMaybe, mightExist)+import Paths_ghc_mod (version)++import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.Caching.Types+import Language.Haskell.GhcMod.Logging++-- | Cache a MonadIO action with proper invalidation.+cached :: forall m a d. (Gm m, MonadIO m, Serialize a, Eq d, Serialize d, Show d)+ => FilePath -- ^ Directory to prepend to 'cacheFile'+ -> Cached m GhcModState d a -- ^ Cache descriptor+ -> d+ -> m a+cached dir cd d = do+ mcc <- readCache+ tcfile <- liftIO $ timeMaybe (cacheFile cd)+ case mcc of+ Nothing ->+ writeCache (TimedCacheFiles tcfile []) Nothing "cache missing or unreadable"+ Just (ifs, d', a) | d /= d' -> do+ tcf <- timeCacheInput dir (cacheFile cd) ifs+ writeCache tcf (Just a) $ "input data changed" -- ++ " was: " ++ show d ++ " is: " ++ show d'+ Just (ifs, _, a) -> do+ tcf <- timeCacheInput dir (cacheFile cd) ifs+ case invalidatingInputFiles tcf of+ Just [] -> return a+ Just _ -> writeCache tcf (Just a) "input files changed"+ Nothing -> writeCache tcf (Just a) "cache missing, existed a sec ago WTF?"++ where+ cacheHeader = BS8.pack $ "Written by ghc-mod " ++ showVersion version ++ "\n"++ writeCache tcf ma cause = do+ (ifs', a) <- (cachedAction cd) tcf d ma+ gmLog GmDebug "" $ (text "regenerating cache") <+>: text (cacheFile cd)+ <+> parens (text cause)+ case cacheLens cd of+ Nothing -> return ()+ Just label -> do+ gmLog GmDebug "" $ (text "writing memory cache") <+>: text (cacheFile cd)+ setLabel label $ Just (ifs', d, a)++ liftIO $ BS.writeFile (dir </> cacheFile cd) $+ BS.append cacheHeader $ encode (ifs', d, a)+ return a++ setLabel l x = do+ s <- gmsGet+ gmsPut $ set l x s++ readCache :: m (Maybe ([FilePath], d, a))+ readCache = runMaybeT $ do+ case cacheLens cd of+ Just label -> do+ c <- MaybeT (get label `liftM` gmsGet) `mplus` readCacheFromFile+ setLabel label $ Just c+ return c+ Nothing ->+ readCacheFromFile++ readCacheFromFile = do+ f <- MaybeT $ liftIO $ mightExist $ cacheFile cd+ readCacheFromFile' f++ readCacheFromFile' f = MaybeT $ do+ gmLog GmDebug "" $ (text "reading cache") <+>: text (cacheFile cd)+ cc <- liftIO $ BS.readFile f+ case first BS8.words $ BS8.span (/='\n') cc of+ (["Written", "by", "ghc-mod", ver], rest)+ | BS8.unpack ver == showVersion version ->+ return $ either (const Nothing) Just $ decode $ BS.drop 1 rest+ _ -> return Nothing++timeCacheInput :: MonadIO m => FilePath -> FilePath -> [FilePath] -> m TimedCacheFiles+timeCacheInput dir cfile ifs = liftIO $ do+ -- TODO: is checking the times this way around race free?+ ins <- (timeMaybe . (dir </>)) `mapM` ifs+ mtcfile <- timeMaybe cfile+ return $ TimedCacheFiles mtcfile (catMaybes ins)++invalidatingInputFiles :: TimedCacheFiles -> Maybe [FilePath]+invalidatingInputFiles tcf =+ case tcCacheFile tcf of+ Nothing -> Nothing+ Just tcfile -> Just $ map tfPath $+ -- get input files older than tcfile+ filter (tcfile<) $ tcFiles tcf
+ Language/Haskell/GhcMod/Caching/Types.hs view
@@ -0,0 +1,52 @@+module Language.Haskell.GhcMod.Caching.Types where++import Utils+import Data.Label+import Data.Version+import Distribution.Helper++type CacheContents d a = Maybe ([FilePath], d, a)+type CacheLens s d a = s :-> CacheContents d a++data Cached m s d a = Cached {+ cacheFile :: FilePath,+ cacheLens :: Maybe (CacheLens s d a),+ cachedAction :: TimedCacheFiles+ -> d+ -> Maybe a+ -> m ([FilePath], a)++ -- ^ @cachedAction tcf data ma@+ --+ -- * @tcf@: Input file timestamps. Not technically necessary, just an+ -- optimizazion when knowing which input files changed can make updating the+ -- cache faster+ --+ -- * @data@: Arbitrary static input data to cache action. Can be used to+ -- invalidate the cache using something other than file timestamps+ -- i.e. environment tool version numbers+ --+ -- * @ma@: Cached data if it existed+ --+ -- Returns:+ --+ -- * @fst@: Input files used in generating the cache+ --+ -- * @snd@: Cache data, will be stored alongside the static input data in the+ -- 'cacheFile'+ --+ -- The cached action, will only run if one of the following is true:+ --+ -- * 'cacheFile' doesn\'t exist yet+ -- * 'cacheFile' exists and 'inputData' changed+ -- * any files returned by the cached action changed+ }++data TimedCacheFiles = TimedCacheFiles {+ tcCacheFile :: Maybe TimedFile,+ -- ^ 'cacheFile' timestamp+ tcFiles :: [TimedFile]+ -- ^ Timestamped files returned by the cached action+ }++type ChCacheData = (Programs, FilePath, FilePath, (Version, [Char]))
Language/Haskell/GhcMod/CaseSplit.hs view
@@ -8,17 +8,24 @@ import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Text.IO as T (readFile)+import System.FilePath+ import qualified DataCon as Ty-import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G+import Outputable (PprStyle)+import qualified TyCon as Ty+import qualified Type as Ty+import Exception+ import Language.Haskell.GhcMod.Convert+import Language.Haskell.GhcMod.DynFlags import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils-import Outputable (PprStyle)-import qualified TyCon as Ty-import qualified Type as Ty+import Language.Haskell.GhcMod.Doc+import Language.Haskell.GhcMod.Logging+import Language.Haskell.GhcMod.Types ---------------------------------------------------------------- -- CASE SPLITTING@@ -38,23 +45,29 @@ -> Int -- ^ Line number. -> Int -- ^ Column number. -> GhcModT m String-splits file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- whenFound' opt (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of- (SplitInfo varName bndLoc (varLoc,varT) _matches) -> do- let varName' = showName dflag style varName -- Convert name to string- text <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $- getTyCons dflag style varName varT)- return (fourInts bndLoc, text)- (TySplitInfo varName bndLoc (varLoc,varT)) -> do- let varName' = showName dflag style varName -- Convert name to string- text <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $- getTyCons dflag style varName varT)- return (fourInts bndLoc, text)- handler (SomeException _) = emptyResult =<< options+splits file lineNo colNo =+ ghandle handler $ runGmlT' [Left file] deferErrors $ do+ opt <- options+ crdl <- cradle+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- Gap.fileModSummary (cradleCurrentDir crdl </> file)+ whenFound' opt (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of+ (SplitInfo varName bndLoc (varLoc,varT) _matches) -> do+ let varName' = showName dflag style varName -- Convert name to string+ t <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $+ getTyCons dflag style varName varT)+ return (fourInts bndLoc, t)+ (TySplitInfo varName bndLoc (varLoc,varT)) -> do+ let varName' = showName dflag style varName -- Convert name to string+ t <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $+ getTyCons dflag style varName varT)+ return (fourInts bndLoc, t)+ where+ handler (SomeException ex) = do+ gmLog GmDebug "splits" $+ text "" $$ nest 4 (showDoc ex)+ emptyResult =<< options ---------------------------------------------------------------- -- a. Code for getting the information of the variable@@ -79,7 +92,11 @@ varT <- Gap.getType tcm varPat' -- Finally we get the type of the var case varT of Just varT' ->+#if __GLASGOW_HASKELL__ >= 710+ let (L matchL (G.Match _ _ _ (G.GRHSs rhsLs _))) = match+#else let (L matchL (G.Match _ _ (G.GRHSs rhsLs _))) = match+#endif in return $ Just (SplitInfo (getPatternVarName varPat') matchL varT' (map G.getLoc rhsLs) ) _ -> return Nothing @@ -178,15 +195,16 @@ ---------------------------------------------------------------- -- c. Code for performing the case splitting -genCaseSplitTextFile :: GhcMonad m => FilePath -> SplitToTextInfo -> m String+genCaseSplitTextFile :: (MonadIO m, GhcMonad m) =>+ FilePath -> SplitToTextInfo -> m String genCaseSplitTextFile file info = liftIO $ do- text <- T.readFile file- return $ getCaseSplitText (T.lines text) info+ t <- T.readFile file+ return $ getCaseSplitText (T.lines t) info getCaseSplitText :: [T.Text] -> SplitToTextInfo -> String-getCaseSplitText text (SplitToTextInfo { sVarName = sVN, sBindingSpan = sBS+getCaseSplitText t (SplitToTextInfo { sVarName = sVN, sBindingSpan = sBS , sVarSpan = sVS, sTycons = sT }) =- let bindingText = getBindingText text sBS+ let bindingText = getBindingText t sBS difference = srcSpanDifference sBS sVS replaced = map (replaceVarWithTyCon bindingText difference sVN) sT -- The newly generated bindings need to be indented to align with the@@ -195,9 +213,9 @@ in T.unpack $ T.intercalate (T.pack "\n") (concat replaced') getBindingText :: [T.Text] -> SrcSpan -> [T.Text]-getBindingText text srcSpan =+getBindingText t srcSpan = let Just (sl,sc,el,ec) = Gap.getSrcSpan srcSpan- lines_ = drop (sl - 1) $ take el text+ lines_ = drop (sl - 1) $ take el t in if sl == el then -- only one line [T.drop (sc - 1) $ T.take ec $ head lines_]@@ -212,7 +230,7 @@ in (vsl - bsl, vsc - bsc, vel - bsl, vec - bsc) -- assume variable in one line replaceVarWithTyCon :: [T.Text] -> (Int,Int,Int,Int) -> String -> String -> [T.Text]-replaceVarWithTyCon text (vsl,vsc,_,vec) varname tycon =+replaceVarWithTyCon t (vsl,vsc,_,vec) varname tycon = let tycon' = if ' ' `elem` tycon || ':' `elem` tycon then "(" ++ tycon ++ ")" else tycon lengthDiff = length tycon' - length varname tycon'' = T.pack $ if lengthDiff < 0 then tycon' ++ replicate (-lengthDiff) ' ' else tycon'@@ -222,7 +240,7 @@ else if n == vsl then T.take vsc line `T.append` tycon'' `T.append` T.drop vec line else T.replicate spacesToAdd (T.pack " ") `T.append` line)- [0 ..] text+ [0 ..] t indentBindingTo :: SrcSpan -> [T.Text] -> [T.Text] indentBindingTo bndLoc binds =
Language/Haskell/GhcMod/Check.hs view
@@ -5,12 +5,12 @@ , expand ) where -import Control.Applicative ((<$>))+import Control.Applicative+import Prelude import Language.Haskell.GhcMod.DynFlags import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Logger-import Language.Haskell.GhcMod.Monad (IOish, GhcModT)-import Language.Haskell.GhcMod.Target (setTargetFiles)+import Language.Haskell.GhcMod.Monad ---------------------------------------------------------------- @@ -29,15 +29,12 @@ check :: IOish m => [FilePath] -- ^ The target files. -> GhcModT m (Either String String)-{--check fileNames = overrideGhcUserOptions $ \ghcOpts -> do- withLogger (setAllWarningFlags . setNoMaxRelevantBindings . Gap.setWarnTypedHoles . Gap.setDeferTypeErrors) $ do- _ <- G.setSessionDynFlags =<< addCmdOpts ghcOpts =<< G.getSessionDynFlags- setTargetFiles fileNames--}-check fileNames =- withLogger (setAllWarningFlags . setNoMaxRelevantBindings) $- setTargetFiles fileNames+check files =+ runGmlTWith+ (map Left files)+ return+ ((fmap fst <$>) . withLogger setNoMaxRelevantBindings)+ (return ()) ---------------------------------------------------------------- @@ -51,8 +48,10 @@ ---------------------------------------------------------------- -- | Expanding Haskell Template.-expand :: IOish m- => [FilePath] -- ^ The target files.- -> GhcModT m (Either String String)-expand fileNames = withLogger (Gap.setDumpSplices . setNoWarningFlags) $- setTargetFiles fileNames+expand :: IOish m => [FilePath] -> GhcModT m (Either String String)+expand files =+ runGmlTWith+ (map Left files)+ return+ ((fmap fst <$>) . withLogger (Gap.setDumpSplices . setNoWarningFlags))+ (return ())
Language/Haskell/GhcMod/Convert.hs view
@@ -1,11 +1,12 @@-{-# LANGUAGE FlexibleInstances, FlexibleContexts, OverlappingInstances #-}+{-# LANGUAGE CPP, FlexibleInstances, FlexibleContexts #-} module Language.Haskell.GhcMod.Convert (convert, convert', emptyResult, whenFound, whenFound') where -import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types -import Control.Applicative ((<$>))+import Control.Applicative+import Prelude type Builder = String -> String @@ -23,11 +24,11 @@ inter _ [] = id inter c bs = foldr1 (\x y -> x . (c:) . y) bs -convert' :: (ToString a, IOish m) => a -> GhcModT m String+convert' :: (ToString a, IOish m, GmEnv m) => a -> m String convert' x = flip convert x <$> options convert :: ToString a => Options -> a -> String-convert opt@Options { outputStyle = LispStyle } x = toLisp opt x "\n"+convert opt@Options { outputStyle = LispStyle } x = toLisp opt x "\n" convert opt@Options { outputStyle = PlainStyle } x | str == "\n" = "" | otherwise = str@@ -35,8 +36,8 @@ str = toPlain opt x "\n" class ToString a where- toLisp :: Options -> a -> Builder- toPlain :: Options -> a -> Builder+ toLisp :: Options -> a -> Builder+ toPlain :: Options -> a -> Builder lineSep :: Options -> String lineSep opt = interpret lsep@@ -51,8 +52,8 @@ -- >>> toPlain defaultOptions "foo" "" -- "foo" instance ToString String where- toLisp opt = quote opt- toPlain opt = replace '\n' (lineSep opt)+ toLisp opt = quote opt+ toPlain opt = replace '\n' (lineSep opt) -- | --@@ -61,9 +62,13 @@ -- >>> toPlain defaultOptions ["foo", "bar", "baz"] "" -- "foo\nbar\nbaz" instance ToString [String] where- toLisp opt = toSexp1 opt- toPlain opt = inter '\n' . map (toPlain opt)+ toLisp opt = toSexp1 opt+ toPlain opt = inter '\n' . map (toPlain opt) +instance ToString [ModuleString] where+ toLisp opt = toLisp opt . map getModuleString+ toPlain opt = toPlain opt . map getModuleString+ -- | -- -- >>> let inp = [((1,2,3,4),"foo"),((5,6,7,8),"bar")] :: [((Int,Int,Int,Int),String)]@@ -72,23 +77,23 @@ -- >>> toPlain defaultOptions inp "" -- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\"" instance ToString [((Int,Int,Int,Int),String)] where- toLisp opt = toSexp2 . map toS- where- toS x = ('(' :) . tupToString opt x . (')' :)- toPlain opt = inter '\n' . map (tupToString opt)+ toLisp opt = toSexp2 . map toS+ where+ toS x = ('(' :) . tupToString opt x . (')' :)+ toPlain opt = inter '\n' . map (tupToString opt) instance ToString ((Int,Int,Int,Int),String) where- toLisp opt x = ('(' :) . tupToString opt x . (')' :)- toPlain opt x = tupToString opt x+ toLisp opt x = ('(' :) . tupToString opt x . (')' :)+ toPlain opt x = tupToString opt x instance ToString ((Int,Int,Int,Int),[String]) where- toLisp opt (x,s) = ('(' :) . fourIntsToString opt x .- (' ' :) . toLisp opt s . (')' :)- toPlain opt (x,s) = fourIntsToString opt x . ('\n' :) . toPlain opt s+ toLisp opt (x,s) = ('(' :) . fourIntsToString opt x .+ (' ' :) . toLisp opt s . (')' :)+ toPlain opt (x,s) = fourIntsToString opt x . ('\n' :) . toPlain opt s instance ToString (String, (Int,Int,Int,Int),[String]) where- toLisp opt (s,x,y) = toSexp2 [toLisp opt s, ('(' :) . fourIntsToString opt x . (')' :), toLisp opt y]- toPlain opt (s,x,y) = inter '\n' [toPlain opt s, fourIntsToString opt x, toPlain opt y]+ toLisp opt (s,x,y) = toSexp2 [toLisp opt s, ('(' :) . fourIntsToString opt x . (')' :), toLisp opt y]+ toPlain opt (s,x,y) = inter '\n' [toPlain opt s, fourIntsToString opt x, toPlain opt y] toSexp1 :: Options -> [String] -> Builder toSexp1 opt ss = ('(' :) . inter ' ' (map (quote opt) ss) . (')' :)
Language/Haskell/GhcMod/Cradle.hs view
@@ -1,19 +1,22 @@ module Language.Haskell.GhcMod.Cradle ( findCradle , findCradle'- , findCradleWithoutSandbox+ , findSpecCradle , cleanupCradle ) where -import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.PathsAndFiles+import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils -import Control.Exception.IOChoice ((||>))-import System.Directory (getCurrentDirectory, removeDirectoryRecursive)-import System.FilePath (takeDirectory)-+import Control.Applicative+import Control.Monad+import Control.Monad.Trans.Maybe+import Data.Maybe+import System.Directory+import System.FilePath+import Prelude ---------------------------------------------------------------- @@ -25,51 +28,62 @@ findCradle = findCradle' =<< getCurrentDirectory findCradle' :: FilePath -> IO Cradle-findCradle' dir = cabalCradle dir ||> sandboxCradle dir ||> plainCradle dir+findCradle' dir = run $ do+ (cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir)+ where run a = fillTempDir =<< (fromJust <$> runMaybeT a) +findSpecCradle :: FilePath -> IO Cradle+findSpecCradle dir = do+ let cfs = [cabalCradle, sandboxCradle]+ cs <- catMaybes <$> mapM (runMaybeT . ($ dir)) cfs+ gcs <- filterM isNotGmCradle cs+ fillTempDir =<< case gcs of+ [] -> fromJust <$> runMaybeT (plainCradle dir)+ c:_ -> return c+ where+ isNotGmCradle :: Cradle -> IO Bool+ isNotGmCradle crdl = do+ not <$> doesFileExist (cradleRootDir crdl </> "ghc-mod.cabal")+ cleanupCradle :: Cradle -> IO () cleanupCradle crdl = removeDirectoryRecursive $ cradleTempDir crdl -cabalCradle :: FilePath -> IO Cradle+fillTempDir :: MonadIO m => Cradle -> m Cradle+fillTempDir crdl = do+ tmpDir <- liftIO $ newTempDir (cradleRootDir crdl)+ return crdl { cradleTempDir = tmpDir }++cabalCradle :: FilePath -> MaybeT IO Cradle cabalCradle wdir = do- Just cabalFile <- findCabalFile wdir+ cabalFile <- MaybeT $ findCabalFile wdir+ let cabalDir = takeDirectory cabalFile- pkgDbStack <- getPackageDbStack cabalDir- tmpDir <- newTempDir cabalDir+ return Cradle {- cradleCurrentDir = wdir+ cradleProjectType = CabalProject+ , cradleCurrentDir = wdir , cradleRootDir = cabalDir- , cradleTempDir = tmpDir+ , cradleTempDir = error "tmpDir" , cradleCabalFile = Just cabalFile- , cradlePkgDbStack = pkgDbStack } -sandboxCradle :: FilePath -> IO Cradle+sandboxCradle :: FilePath -> MaybeT IO Cradle sandboxCradle wdir = do- Just sbDir <- getSandboxDb wdir- pkgDbStack <- getPackageDbStack sbDir- tmpDir <- newTempDir sbDir+ sbDir <- MaybeT $ findCabalSandboxDir wdir return Cradle {- cradleCurrentDir = wdir+ cradleProjectType = SandboxProject+ , cradleCurrentDir = wdir , cradleRootDir = sbDir- , cradleTempDir = tmpDir+ , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing- , cradlePkgDbStack = pkgDbStack } -plainCradle :: FilePath -> IO Cradle+plainCradle :: FilePath -> MaybeT IO Cradle plainCradle wdir = do- tmpDir <- newTempDir wdir- return Cradle {- cradleCurrentDir = wdir+ return $ Cradle {+ cradleProjectType = PlainProject+ , cradleCurrentDir = wdir , cradleRootDir = wdir- , cradleTempDir = tmpDir+ , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing- , cradlePkgDbStack = [GlobalDb, UserDb] }---- Just for testing-findCradleWithoutSandbox :: IO Cradle-findCradleWithoutSandbox = do- cradle <- findCradle- return cradle { cradlePkgDbStack = [GlobalDb]} -- FIXME
Language/Haskell/GhcMod/Debug.hs view
@@ -1,39 +1,127 @@-module Language.Haskell.GhcMod.Debug (debugInfo, rootInfo) where+module Language.Haskell.GhcMod.Debug (debugInfo, rootInfo, componentInfo) where -import Control.Applicative ((<$>))-import Data.List (intercalate)-import Data.Maybe (isJust, fromJust)+import Control.Arrow (first)+import Control.Applicative+import Control.Monad+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Char+import Data.List.Split+import Text.PrettyPrint import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Internal+import Language.Haskell.GhcMod.Target+import Language.Haskell.GhcMod.Pretty+import Language.Haskell.GhcMod.Utils ---------------------------------------------------------------- -- | Obtaining debug information. debugInfo :: IOish m => GhcModT m String-debugInfo = cradle >>= \c -> convert' =<< do- CompilerOptions gopts incDir pkgs <-- if isJust $ cradleCabalFile c then- fromCabalFile c ||> simpleCompilerOption- else- simpleCompilerOption- return [- "Root directory: " ++ cradleRootDir c- , "Current directory: " ++ cradleCurrentDir c- , "Cabal file: " ++ show (cradleCabalFile c)- , "GHC options: " ++ unwords gopts- , "Include directories: " ++ unwords incDir- , "Dependent packages: " ++ intercalate ", " (map showPkg pkgs)- , "System libraries: " ++ ghcLibDir- ]- where- simpleCompilerOption = options >>= \op ->- return $ CompilerOptions (ghcUserOptions op) [] []- fromCabalFile c = options >>= \opts -> do- pkgDesc <- parseCabalFile c $ fromJust $ cradleCabalFile c- getCompilerOptions (ghcUserOptions opts) c pkgDesc+debugInfo = do+ Options {..} <- options+ Cradle {..} <- cradle + cabal <-+ case cradleProjectType of+ CabalProject -> cabalDebug+ _ -> return []++ pkgOpts <- packageGhcOptions++ return $ unlines $+ [ "Root directory: " ++ cradleRootDir+ , "Current directory: " ++ cradleCurrentDir+ , "GHC Package flags:\n" ++ render (nest 4 $+ fsep $ map text pkgOpts)+ , "GHC System libraries: " ++ ghcLibDir+ , "GHC user options:\n" ++ render (nest 4 $+ fsep $ map text ghcUserOptions)+ ] ++ cabal++cabalDebug :: IOish m => GhcModT m [String]+cabalDebug = do+ Cradle {..} <- cradle+ mcs <- cabalResolvedComponents+ let entrypoints = Map.map gmcEntrypoints mcs+ graphs = Map.map gmcHomeModuleGraph mcs+ opts = Map.map gmcGhcOpts mcs+ srcOpts = Map.map gmcGhcSrcOpts mcs++ return $+ [ "Cabal file: " ++ show cradleCabalFile+ , "Cabal entrypoints:\n" ++ render (nest 4 $+ mapDoc gmComponentNameDoc smpDoc entrypoints)+ , "Cabal components:\n" ++ render (nest 4 $+ mapDoc gmComponentNameDoc graphDoc graphs)+ , "GHC Cabal options:\n" ++ render (nest 4 $+ mapDoc gmComponentNameDoc (fsep . map text) opts)+ , "GHC search path options:\n" ++ render (nest 4 $+ mapDoc gmComponentNameDoc (fsep . map text) srcOpts)+ ]++componentInfo :: IOish m => [String] -> GhcModT m String+componentInfo ts = do+ -- TODO: most of this is copypasta of targetGhcOptions. Factor out more+ -- useful function from there.+ crdl <- cradle+ sefnmn <- Set.fromList `liftM` mapM guessModuleFile ts+ mcs <- cabalResolvedComponents+ let+ mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn+ candidates = findCandidates $ map snd mdlcs+ cn = pickComponent candidates+ opts <- targetGhcOptions crdl sefnmn++ return $ unlines $+ [ "Matching Components:\n" ++ render (nest 4 $+ alistDoc (either text mnDoc) (setDoc gmComponentNameDoc) mdlcs)+ , "Picked Component:\n" ++ render (nest 4 $+ gmComponentNameDoc cn)+ , "GHC Cabal options:\n" ++ render (nest 4 $ fsep $ map text opts)+ ]+ where+ zipMap f l = l `zip` (f `map` l)++guessModuleFile :: MonadIO m => String -> m (Either FilePath ModuleName)+guessModuleFile m+ | (isUpper . head .&&. (all $ all $ isAlphaNum .||. (=='.')) . splitOn ".") m =+ return $ Right $ mkModuleName m+ where+ infixr 1 .||.+ infixr 2 .&&.+ (.||.) = liftA2 (||)+ (.&&.) = liftA2 (&&)++guessModuleFile str = Left `liftM` liftIO (canonFilePath str)++graphDoc :: GmModuleGraph -> Doc+graphDoc GmModuleGraph{..} =+ mapDoc mpDoc smpDoc' gmgGraph+ where+ smpDoc' smp = vcat $ map mpDoc' $ Set.toList smp+ mpDoc' = text . moduleNameString . mpModule++setDoc :: (a -> Doc) -> Set.Set a -> Doc+setDoc f s = vcat $ map f $ Set.toList s++smpDoc :: Set.Set ModulePath -> Doc+smpDoc smp = setDoc mpDoc smp++mpDoc :: ModulePath -> Doc+mpDoc (ModulePath mn fn) = text (moduleNameString mn) <+> parens (text fn)++mnDoc :: ModuleName -> Doc+mnDoc mn = text (moduleNameString mn)++alistDoc :: Ord k => (k -> Doc) -> (a -> Doc) -> [(k, a)] -> Doc+alistDoc fk fa alist = mapDoc fk fa (Map.fromList alist)++mapDoc :: (k -> Doc) -> (a -> Doc) -> Map.Map k a -> Doc+mapDoc kd ad m = vcat $+ map (uncurry ($+$)) $ map (first kd) $ Map.toList $ Map.map (nest 4 . ad) m ---------------------------------------------------------------- -- | Obtaining root information.
Language/Haskell/GhcMod/Doc.hs view
@@ -1,9 +1,8 @@ module Language.Haskell.GhcMod.Doc where -import GHC (DynFlags, GhcMonad)-import qualified GHC as G+import GHC import Language.Haskell.GhcMod.Gap (withStyle, showDocWith)-import Outputable (SDoc, PprStyle, mkUserStyle, Depth(AllTheWay), neverQualify)+import Outputable import Pretty (Mode(..)) showPage :: DynFlags -> PprStyle -> SDoc -> String@@ -12,9 +11,14 @@ showOneLine :: DynFlags -> PprStyle -> SDoc -> String showOneLine dflag style = showDocWith dflag OneLineMode . withStyle dflag style +-- showForUser :: DynFlags -> PrintUnqualified -> SDoc -> String+-- showForUser dflags unqual sdoc =+-- showDocWith dflags PageMode $+-- runSDoc sdoc $ initSDocContext dflags $ mkUserStyle unqual AllTheWay+ getStyle :: GhcMonad m => m PprStyle getStyle = do- unqual <- G.getPrintUnqual+ unqual <- getPrintUnqual return $ mkUserStyle unqual AllTheWay styleUnqualified :: PprStyle
Language/Haskell/GhcMod/DynFlags.hs view
@@ -2,7 +2,7 @@ module Language.Haskell.GhcMod.DynFlags where -import Control.Applicative ((<$>))+import Control.Applicative import Control.Monad (void) import GHC (DynFlags(..), GhcMode(..), GhcLink(..), HscTarget(..)) import qualified GHC as G@@ -11,8 +11,7 @@ import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Types import System.IO.Unsafe (unsafePerformIO)--data Build = CabalPkg | SingleFile deriving Eq+import Prelude setEmptyLogger :: DynFlags -> DynFlags setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return ()@@ -41,37 +40,15 @@ , optLevel = 0 } -setIncludeDirs :: [IncludeDir] -> DynFlags -> DynFlags-setIncludeDirs idirs df = df { importPaths = idirs }--setBuildEnv :: Build -> DynFlags -> DynFlags-setBuildEnv build = setHideAllPackages build . setCabalPackage build---- | With ghc-7.8 this option simply makes GHC print a message suggesting users--- add hiddend packages to the build-depends field in their cabal file when the--- user tries to import a module form a hidden package.-setCabalPackage :: Build -> DynFlags -> DynFlags-setCabalPackage CabalPkg df = Gap.setCabalPkg df-setCabalPackage _ df = df---- | Enable hiding of all package not explicitly exposed (like Cabal does)-setHideAllPackages :: Build -> DynFlags -> DynFlags-setHideAllPackages CabalPkg df = Gap.setHideAllPackages df-setHideAllPackages _ df = df- -- | Parse command line ghc options and add them to the 'DynFlags' passed addCmdOpts :: GhcMonad m => [GHCOption] -> DynFlags -> m DynFlags addCmdOpts cmdOpts df =- tfst <$> G.parseDynamicFlags df (map G.noLoc cmdOpts)+ fst3 <$> G.parseDynamicFlags df (map G.noLoc cmdOpts) where- tfst (a,_,_) = a+ fst3 (a,_,_) = a ---------------------------------------------------------------- --- | Return the 'DynFlags' currently in use in the GHC session.-getDynamicFlags :: IO DynFlags-getDynamicFlags = G.runGhc (Just libdir) G.getSessionDynFlags- withDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m a@@ -119,3 +96,7 @@ #else setNoMaxRelevantBindings = id #endif++deferErrors :: DynFlags -> Ghc DynFlags+deferErrors df = return $+ Gap.setWarnTypedHoles $ Gap.setDeferTypeErrors $ setNoWarningFlags df
Language/Haskell/GhcMod/Error.hs view
@@ -1,67 +1,173 @@-{-# LANGUAGE TypeFamilies, ScopedTypeVariables, DeriveDataTypeable #-}+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE ExistentialQuantification #-} module Language.Haskell.GhcMod.Error ( GhcModError(..)+ , GMConfigStateFileError(..)+ , GmError , gmeDoc+ , ghcExceptionDoc+ , liftMaybe+ , overrideError , modifyError , modifyError'+ , modifyGmError , tryFix+ , GHandler(..)+ , gcatches , module Control.Monad.Error- , module Exception+ , module Control.Exception ) where -import Control.Monad.Error (MonadError(..), Error(..))+import Control.Arrow+import Control.Exception+import Control.Monad.Error hiding (MonadIO, liftIO)+import qualified Data.Set as Set import Data.List-import Data.Typeable-import Exception+import Data.Version+import System.Process (showCommandForUser) import Text.PrettyPrint+import Text.Printf -data GhcModError = GMENoMsg- -- ^ Unknown error- | GMEString String- -- ^ Some Error with a message. These are produced mostly by- -- 'fail' calls on GhcModT.- | GMEIOException IOException- -- ^ IOExceptions captured by GhcModT's MonadIO instance- | GMECabalConfigure GhcModError- -- ^ Configuring a cabal project failed.- | GMECabalFlags GhcModError- -- ^ Retrieval of the cabal configuration flags failed.- | GMEProcess [String] GhcModError- -- ^ Launching an operating system process failed. The first- -- field is the command.- | GMENoCabalFile- -- ^ No cabal file found.- | GMETooManyCabalFiles [FilePath]- -- ^ Too many cabal files found.- deriving (Eq,Show,Typeable)+import Exception+import Panic+import Config (cProjectVersion, cHostPlatformString)+import Paths_ghc_mod (version) -instance Exception GhcModError+import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Pretty -instance Error GhcModError where- noMsg = GMENoMsg- strMsg = GMEString+type GmError m = MonadError GhcModError m +gmCsfeDoc :: GMConfigStateFileError -> Doc+gmCsfeDoc GMConfigStateFileNoHeader = text $+ "Saved package config file header is missing. "+ ++ "Try re-running the 'configure' command."++gmCsfeDoc GMConfigStateFileBadHeader = text $+ "Saved package config file header is corrupt. "+ ++ "Try re-running the 'configure' command."++gmCsfeDoc GMConfigStateFileNoParse = text $+ "Saved package config file body is corrupt. "+ ++ "Try re-running the 'configure' command."++gmCsfeDoc GMConfigStateFileMissing = text $+ "Run the 'configure' command first."++-- gmCsfeDoc (ConfigStateFileBadVersion oldCabal oldCompiler _) = text $+-- "You need to re-run the 'configure' command. "+-- ++ "The version of Cabal being used has changed (was "+-- ++ display oldCabal ++ ", now "+-- ++ display currentCabalId ++ ")."+-- ++ badCompiler+-- where+-- badCompiler+-- | oldCompiler == currentCompilerId = ""+-- | otherwise =+-- " Additionally the compiler is different (was "+-- ++ display oldCompiler ++ ", now "+-- ++ display currentCompilerId+-- ++ ") which is probably the cause of the problem."+ gmeDoc :: GhcModError -> Doc gmeDoc e = case e of GMENoMsg -> text "Unknown error" GMEString msg -> text msg- GMEIOException ioe ->- text $ show ioe GMECabalConfigure msg ->- text "cabal configure failed: " <> gmeDoc msg+ text "Configuring cabal project failed: " <> gmeDoc msg GMECabalFlags msg ->- text "retrieval of the cabal configuration flags failed: " <> gmeDoc msg- GMEProcess cmd msg ->- text ("launching operating system process `"++unwords cmd++"` failed: ")- <> gmeDoc msg+ text "Retrieval of the cabal configuration flags failed: " <> gmeDoc msg+ GMECabalComponent cn ->+ text "Cabal component " <> quotes (gmComponentNameDoc cn)+ <> text " could not be found."+ GMECabalCompAssignment ctx ->+ text "Could not find a consistent component assignment for modules:" $$+ (nest 4 $ foldr ($+$) empty $ map ctxDoc ctx) $$+ text "" $$+ (if all (Set.null . snd) ctx+ then noComponentSuggestions+ else empty) $$+ text "- To find out which components ghc-mod knows about try:" $$+ nest 4 (backticks $ text "ghc-mod debug")++ where+ noComponentSuggestions =+ text "- Are some of these modules part of a test and or benchmark?\+ \ Try enabling them:" $$+ nest 4 (backticks $ text "cabal configure --enable-tests [--enable-benchmarks]")++ backticks d = char '`' <> d <> char '`'+ ctxDoc = moduleDoc *** compsDoc+ >>> first (<> colon) >>> uncurry (flip hang 4)++ moduleDoc (Left fn) =+ text "File " <> quotes (text fn)+ moduleDoc (Right mdl) =+ text "Module " <> quotes (text $ moduleNameString mdl)++ compsDoc sc | Set.null sc = text "has no known components"+ compsDoc sc = fsep $ punctuate comma $+ map gmComponentNameDoc $ Set.toList sc++ GMEProcess cmd args emsg -> let c = showCommandForUser cmd args in+ case emsg of+ Right err ->+ text (printf "Launching system command `%s` failed: " c)+ <> gmeDoc err+ Left (_out, _err, rv) -> text $+ printf "Launching system command `%s` failed (exited with %d)" c rv GMENoCabalFile -> text "No cabal file found." GMETooManyCabalFiles cfs -> text $ "Multiple cabal files found. Possible cabal files: \"" ++ intercalate "\", \"" cfs ++"\"."+ GMECabalStateFile csfe ->+ gmCsfeDoc csfe +ghcExceptionDoc :: GhcException -> Doc+ghcExceptionDoc e@(CmdLineError _) =+ text $ "<command line>: " ++ showGhcException e ""+ghcExceptionDoc (UsageError str) = strDoc str+ghcExceptionDoc (Panic msg) = vcat $ map text $ lines $ printf "\+\GHC panic! (the 'impossible' happened)\n\+\ ghc-mod version %s\n\+\ GHC library version %s for %s:\n\+\ %s\n\+\\n\+\Please report this as a bug: %s\n"+ gmVer ghcVer platform msg url+ where+ gmVer = showVersion version+ ghcVer = cProjectVersion+ platform = cHostPlatformString+ url = "https://github.com/kazu-yamamoto/ghc-mod/issues" :: String++ghcExceptionDoc e = text $ showGhcException e ""+++liftMaybe :: MonadError e m => e -> m (Maybe a) -> m a+liftMaybe e action = maybe (throwError e) return =<< action++overrideError :: MonadError e m => e -> m a -> m a+overrideError e action = modifyError (const e) action+ modifyError :: MonadError e m => (e -> e) -> m a -> m a modifyError f action = action `catchError` \e -> throwError $ f e @@ -69,6 +175,24 @@ modifyError' :: MonadError e m => m a -> (e -> e) -> m a modifyError' = flip modifyError ++modifyGmError :: (MonadIO m, ExceptionMonad m)+ => (GhcModError -> GhcModError) -> m a -> m a+modifyGmError f a = gcatch a $ \(ex :: GhcModError) -> liftIO $ throwIO (f ex)+ tryFix :: MonadError e m => m a -> (e -> m ()) -> m a-tryFix action fix = do- action `catchError` \e -> fix e >> action+tryFix action f = do+ action `catchError` \e -> f e >> action++data GHandler m a = forall e . Exception e => GHandler (e -> m a)++gcatches :: (MonadIO m, ExceptionMonad m) => m a -> [GHandler m a] -> m a+gcatches io handlers = io `gcatch` gcatchesHandler handlers++gcatchesHandler :: (MonadIO m, ExceptionMonad m)+ => [GHandler m a] -> SomeException -> m a+gcatchesHandler handlers e = foldr tryHandler (liftIO $ throw e) handlers+ where tryHandler (GHandler handler) res+ = case fromException e of+ Just e' -> handler e'+ Nothing -> res
Language/Haskell/GhcMod/FillSig.hs view
@@ -11,7 +11,8 @@ import Data.Function (on) import Data.List (find, nub, sortBy) import qualified Data.Map as M-import Data.Maybe (isJust, catMaybes)+import Data.Maybe (catMaybes)+import Text.PrettyPrint (($$), text, nest) import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L))@@ -19,8 +20,12 @@ import qualified Name as G import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Convert+import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils+import Language.Haskell.GhcMod.Logging (gmLog)+import Language.Haskell.GhcMod.Pretty (showDoc)+import Language.Haskell.GhcMod.Doc import Language.Haskell.GhcMod.Types import Outputable (PprStyle) import qualified Type as Ty@@ -31,6 +36,10 @@ import qualified Language.Haskell.Exts.Annotated as HE import Djinn.GHC +#if __GLASGOW_HASKELL__ >= 710+import GHC (unLoc)+#endif+ ---------------------------------------------------------------- -- INTIAL CODE FROM FUNCTION OR INSTANCE SIGNATURE ----------------------------------------------------------------@@ -62,22 +71,27 @@ -> Int -- ^ Line number. -> Int -- ^ Column number. -> GhcModT m String-sig file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- whenFound opt (getSignature modSum lineNo colNo) $ \s -> case s of+sig file lineNo colNo =+ runGmlT' [Left file] deferErrors $ ghandle fallback $ do+ opt <- options+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- Gap.fileModSummary file+ whenFound opt (getSignature modSum lineNo colNo) $ \s ->+ case s of Signature loc names ty ->- ("function", fourInts loc, map (initialBody dflag style ty) names)+ ("function", fourInts loc, map (initialBody dflag style ty) names)+ InstanceDecl loc cls ->- ("instance", fourInts loc, map (\x -> initialBody dflag style (G.idType x) x)- (Ty.classMethods cls))+ let body x = initialBody dflag style (G.idType x) x+ in ("instance", fourInts loc, body `map` Ty.classMethods cls)+ TyFamDecl loc name flavour vars -> let (rTy, initial) = initialTyFamString flavour- in (rTy, fourInts loc, [initial ++ initialFamBody dflag style name vars])-- handler (SomeException _) = do+ body = initialFamBody dflag style name vars+ in (rTy, fourInts loc, [initial ++ body])+ where+ fallback (SomeException _) = do opt <- options -- Code cannot be parsed by ghc module -- Fallback: try to get information via haskell-src-exts@@ -97,7 +111,11 @@ p@ParsedModule{pm_parsed_source = ps} <- G.parseModule modSum -- Inspect the parse tree to find the signature case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl G.RdrName] of+#if __GLASGOW_HASKELL__ >= 710+ [L loc (G.SigD (Ty.TypeSig names (L _ ty) _))] ->+#else [L loc (G.SigD (Ty.TypeSig names (L _ ty)))] ->+#endif -- We found a type signature return $ Just $ Signature loc (map G.unLoc names) ty [L _ (G.InstD _)] -> do@@ -125,9 +143,14 @@ G.TypeFamily -> Open G.DataFamily -> Data #endif-#if __GLASGOW_HASKELL__ >= 706++#if __GLASGOW_HASKELL__ >= 710 getTyFamVarName x = case x of L _ (G.UserTyVar n) -> n+ L _ (G.KindedTyVar (G.L _ n) _) -> n+#elif __GLASGOW_HASKELL__ >= 706+ getTyFamVarName x = case x of+ L _ (G.UserTyVar n) -> n L _ (G.KindedTyVar n _) -> n #else getTyFamVarName x = case x of -- In GHC 7.4, HsTyVarBndr's have an extra arg@@ -144,7 +167,8 @@ return $ InstanceDecl loc cls -- Get signature from haskell-src-exts-getSignatureFromHE :: GhcMonad m => FilePath -> Int -> Int -> m (Maybe HESigInfo)+getSignatureFromHE :: (MonadIO m, GhcMonad m) =>+ FilePath -> Int -> Int -> m (Maybe HESigInfo) getSignatureFromHE file lineNo colNo = do presult <- liftIO $ HE.parseFile file return $ case presult of@@ -220,9 +244,11 @@ initialHead1 fname args elts = case initialBodyArgs1 args elts of [] -> fname- arglist -> if isSymbolName fname- then head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)- else fname ++ " " ++ unwords arglist+ arglist+ | isSymbolName fname ->+ head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)+ | otherwise ->+ fname ++ " " ++ unwords arglist initialBodyArgs1 :: [FnArg] -> [String] -> [String] initialBodyArgs1 args elts = take (length args) elts@@ -238,12 +264,24 @@ instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where getFnName dflag style name = showOccName dflag style $ Gap.occName name- getFnArgs (G.HsForAllTy _ _ _ (L _ iTy)) = getFnArgs iTy+#if __GLASGOW_HASKELL__ >= 710+ getFnArgs (G.HsForAllTy _ _ _ _ (L _ iTy))+#else+ getFnArgs (G.HsForAllTy _ _ _ (L _ iTy))+#endif+ = getFnArgs iTy+ getFnArgs (G.HsParTy (L _ iTy)) = getFnArgs iTy getFnArgs (G.HsFunTy (L _ lTy) (L _ rTy)) = (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy where fnarg ty = case ty of- (G.HsForAllTy _ _ _ (L _ iTy)) -> fnarg iTy+#if __GLASGOW_HASKELL__ >= 710+ (G.HsForAllTy _ _ _ _ (L _ iTy)) ->+#else+ (G.HsForAllTy _ _ _ (L _ iTy)) ->+#endif+ fnarg iTy+ (G.HsParTy (L _ iTy)) -> fnarg iTy (G.HsFunTy _ _) -> True _ -> False@@ -301,48 +339,61 @@ -> Int -- ^ Column number. -> Expression -- ^ A Haskell expression. -> GhcModT m String-refine file lineNo colNo expr = ghandle handler body+refine file lineNo colNo (Expression expr) =+ ghandle handler $+ runGmlT' [Left file] deferErrors $ do+ opt <- options+ style <- getStyle+ dflag <- G.getSessionDynFlags+ modSum <- Gap.fileModSummary file+ p <- G.parseModule modSum+ tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p+ ety <- G.exprType expr+ whenFound opt (findVar dflag style tcm tcs lineNo colNo) $+ \(loc, name, rty, paren) ->+ let eArgs = getFnArgs ety+ rArgs = getFnArgs rty+ diffArgs' = length eArgs - length rArgs+ diffArgs = if diffArgs' < 0 then 0 else diffArgs'+ iArgs = take diffArgs eArgs+ txt = initialHead1 expr iArgs (infinitePrefixSupply name)+ in (fourInts loc, doParen paren txt) where- body = inModuleContext file $ \dflag style -> do- opt <- options- modSum <- Gap.fileModSummary file- p <- G.parseModule modSum- tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p- ety <- G.exprType expr- whenFound opt (findVar dflag style tcm tcs lineNo colNo) $- \(loc, name, rty, paren) ->- let eArgs = getFnArgs ety- rArgs = getFnArgs rty- diffArgs' = length eArgs - length rArgs- diffArgs = if diffArgs' < 0 then 0 else diffArgs'- iArgs = take diffArgs eArgs- text = initialHead1 expr iArgs (infinitePrefixSupply name)- in (fourInts loc, doParen paren text)-- handler (SomeException _) = emptyResult =<< options+ handler (SomeException ex) = do+ gmLog GmDebug "refining" $+ text "" $$ nest 4 (showDoc ex)+ emptyResult =<< options -- Look for the variable in the specified position-findVar :: GhcMonad m => DynFlags -> PprStyle- -> G.TypecheckedModule -> G.TypecheckedSource- -> Int -> Int -> m (Maybe (SrcSpan, String, Type, Bool))+findVar+ :: GhcMonad m+ => DynFlags+ -> PprStyle+ -> G.TypecheckedModule+ -> G.TypecheckedSource+ -> Int+ -> Int+ -> m (Maybe (SrcSpan, String, Type, Bool)) findVar dflag style tcm tcs lineNo colNo =- let lst = sortBy (cmp `on` G.getLoc) $- listifySpans tcs (lineNo, colNo) :: [G.LHsExpr Id]- in case lst of- e@(L _ (G.HsVar i)):others ->- do tyInfo <- Gap.getType tcm e- let name = getFnName dflag style i- if (name == "undefined" || head name == '_') && isJust tyInfo- then let Just (s,t) = tyInfo- b = case others of -- If inside an App, we need- -- parenthesis- [] -> False- L _ (G.HsApp (L _ a1) (L _ a2)):_ ->- isSearchedVar i a1 || isSearchedVar i a2- _ -> False- in return $ Just (s, name, t, b)- else return Nothing- _ -> return Nothing+ case lst of+ e@(L _ (G.HsVar i)):others -> do+ tyInfo <- Gap.getType tcm e+ case tyInfo of+ Just (s, typ)+ | name == "undefined" || head name == '_' ->+ return $ Just (s, name, typ, b)+ where+ name = getFnName dflag style i+ -- If inside an App, we need parenthesis+ b = case others of+ L _ (G.HsApp (L _ a1) (L _ a2)):_ ->+ isSearchedVar i a1 || isSearchedVar i a2+ _ -> False+ _ -> return Nothing+ _ -> return Nothing+ where+ lst :: [G.LHsExpr Id]+ lst = sortBy (cmp `on` G.getLoc) $ listifySpans tcs (lineNo, colNo) infinitePrefixSupply :: String -> [String] infinitePrefixSupply "undefined" = repeat "undefined"@@ -366,10 +417,11 @@ -> Int -- ^ Line number. -> Int -- ^ Column number. -> GhcModT m String-auto file lineNo colNo = ghandle handler body- where- body = inModuleContext file $ \dflag style -> do+auto file lineNo colNo =+ ghandle handler $ runGmlT' [Left file] deferErrors $ do opt <- options+ style <- getStyle+ dflag <- G.getSessionDynFlags modSum <- Gap.fileModSummary file p <- G.parseModule modSum tcm@TypecheckedModule {@@ -395,8 +447,11 @@ djinns <- djinn True (Just minfo) env rty (Max 10) 100000 return ( fourInts loc , map (doParen paren) $ nub (djinnsEmpty ++ djinns))-- handler (SomeException _) = emptyResult =<< options+ where+ handler (SomeException ex) = do+ gmLog GmDebug "auto-refining" $+ text "" $$ nest 4 (showDoc ex)+ emptyResult =<< options -- Functions we do not want in completions notWantedFuns :: [String]@@ -443,7 +498,11 @@ #else :: [G.LMatch Id] #endif+#if __GLASGOW_HASKELL__ >= 710+ (L _ (G.Match _ pats _ _):_) = m+#else (L _ (G.Match pats _ _):_) = m+#endif in (funId, pats) _ -> (error "This should never happen", []) @@ -478,7 +537,13 @@ getBindingsForRecPat (Ty.InfixCon (L _ a1) (L _ a2)) = M.union (getBindingsForPat a1) (getBindingsForPat a2) getBindingsForRecPat (Ty.RecCon (Ty.HsRecFields { Ty.rec_flds = fields })) =- getBindingsForRecFields fields- where getBindingsForRecFields [] = M.empty- getBindingsForRecFields (Ty.HsRecField {Ty.hsRecFieldArg = (L _ a)}:fs) =- M.union (getBindingsForPat a) (getBindingsForRecFields fs)+ getBindingsForRecFields (map unLoc' fields)+ where+#if __GLASGOW_HASKELL__ >= 710+ unLoc' = unLoc+#else+ unLoc' = id+#endif+ getBindingsForRecFields [] = M.empty+ getBindingsForRecFields (Ty.HsRecField {Ty.hsRecFieldArg = (L _ a)}:fs) =+ M.union (getBindingsForPat a) (getBindingsForRecFields fs)
Language/Haskell/GhcMod/Find.hs view
@@ -1,9 +1,8 @@-{-# LANGUAGE CPP, BangPatterns #-}+{-# LANGUAGE CPP, BangPatterns, DoAndIfThenElse #-} module Language.Haskell.GhcMod.Find #ifndef SPEC- (- Symbol+ ( Symbol , SymbolDb , loadSymbolDb , lookupSymbol@@ -15,65 +14,51 @@ #endif where -import Control.Applicative ((<$>))+import Control.Applicative import Control.Monad (when, void) import Data.Function (on) import Data.List (groupBy, sort)-import Data.Maybe (fromMaybe) import qualified GHC as G import Language.Haskell.GhcMod.Convert+import Language.Haskell.GhcMod.Gap (listVisibleModules) import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils-import Language.Haskell.GhcMod.PathsAndFiles+import Language.Haskell.GhcMod.World (timedPackageCaches)+import Language.Haskell.GhcMod.Output import Name (getOccString)+import Module (moduleName) import System.Directory (doesFileExist, getModificationTime)-import System.FilePath ((</>), takeDirectory)+import System.FilePath ((</>)) import System.IO--#ifndef MIN_VERSION_containers-#define MIN_VERSION_containers(x,y,z) 1-#endif+import Prelude -#if MIN_VERSION_containers(0,5,0)-import Data.Map.Strict (Map)-import qualified Data.Map.Strict as M-#else import Data.Map (Map) import qualified Data.Map as M-#endif ---------------------------------------------------------------- -- | Type of function and operation names. type Symbol = String -- | Database from 'Symbol' to \['ModuleString'\].-data SymbolDb = SymbolDb {- table :: Map Symbol [ModuleString]- , packageCachePath :: FilePath+data SymbolDb = SymbolDb+ { table :: Map Symbol [ModuleString] , symbolDbCachePath :: FilePath } deriving (Show) -isOutdated :: SymbolDb -> IO Bool-isOutdated db = symbolDbCachePath db `isOlderThan` packageCachePath db---------------------------------------------------------------------- | When introducing incompatible changes to the 'symbolCache' file format--- increment this version number.-symbolCacheVersion :: Integer-symbolCacheVersion = 0---- | Filename of the symbol table cache file.-symbolCache :: String-symbolCache = "ghc-mod-"++ show symbolCacheVersion ++".cache"+isOutdated :: IOish m => SymbolDb -> GhcModT m Bool+isOutdated db =+ (liftIO . isOlderThan (symbolDbCachePath db)) =<< timedPackageCaches ---------------------------------------------------------------- -- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\] -- which will be concatenated. 'loadSymbolDb' is called internally. findSymbol :: IOish m => Symbol -> GhcModT m String-findSymbol sym = loadSymbolDb >>= lookupSymbol sym+findSymbol sym = do+ tmpdir <- cradleTempDir <$> cradle+ loadSymbolDb tmpdir >>= lookupSymbol sym -- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\] -- which will be concatenated.@@ -81,25 +66,25 @@ lookupSymbol sym db = convert' $ lookupSym sym db lookupSym :: Symbol -> SymbolDb -> [ModuleString]-lookupSym sym db = fromMaybe [] $ M.lookup sym $ table db+lookupSym sym db = M.findWithDefault [] sym $ table db --------------------------------------------------------------- -- | Loading a file and creates 'SymbolDb'.-loadSymbolDb :: IOish m => GhcModT m SymbolDb-loadSymbolDb = do- ghcMod <- liftIO ghcModExecutable- tmpdir <- cradleTempDir <$> cradle- file <- chop <$> readProcess' ghcMod ["dumpsym", tmpdir]- !db <- M.fromAscList . map conv . lines <$> liftIO (readFile file)- return $ SymbolDb {- table = db- , packageCachePath = takeDirectory file </> packageCache- , symbolDbCachePath = file- }+loadSymbolDb :: IOish m => FilePath -> GhcModT m SymbolDb+loadSymbolDb dir = do+ ghcMod <- liftIO ghcModExecutable+ readProc <- gmReadProcess+ file <- liftIO $ chop <$> readProc ghcMod ["dumpsym", dir] ""+ !db <- M.fromAscList . map conv . lines <$> liftIO (readFile file)+ return $ SymbolDb+ { table = db+ , symbolDbCachePath = file+ } where- conv :: String -> (Symbol,[ModuleString])+ conv :: String -> (Symbol, [ModuleString]) conv = read+ chop :: String -> String chop "" = "" chop xs = init xs @@ -112,54 +97,52 @@ dumpSymbol :: IOish m => FilePath -> GhcModT m String dumpSymbol dir = do- let cache = dir </> symbolCache- pkgdb = dir </> packageCache-- create <- liftIO $ cache `isOlderThan` pkgdb- when create $ (liftIO . writeSymbolCache cache) =<< getSymbolTable+ create <- (liftIO . isOlderThan cache) =<< timedPackageCaches+ runGmPkgGhc $ do+ when create $+ liftIO . writeSymbolCache cache =<< getGlobalSymbolTable return $ unlines [cache]+ where+ cache = dir </> symbolCacheFile writeSymbolCache :: FilePath- -> [(Symbol,[ModuleString])]+ -> [(Symbol, [ModuleString])] -> IO () writeSymbolCache cache sm = void . withFile cache WriteMode $ \hdl ->- mapM (hPrint hdl) sm--isOlderThan :: FilePath -> FilePath -> IO Bool-isOlderThan cache file = do- exist <- doesFileExist cache- if not exist then- return True- else do- tCache <- getModificationTime cache- tFile <- getModificationTime file- return $ tCache <= tFile -- including equal just in case+ mapM (hPrint hdl) sm --- | Browsing all functions in all system/user modules.-getSymbolTable :: IOish m => GhcModT m [(Symbol,[ModuleString])]-getSymbolTable = do- ghcModules <- G.packageDbModules True- moduleInfos <- mapM G.getModuleInfo ghcModules- let modules = do- m <- ghcModules- let moduleName = G.moduleNameString $ G.moduleName m--- modulePkg = G.packageIdString $ G.modulePackageId m- return moduleName+-- | Check whether given file is older than any file from the given set.+-- Returns True if given file does not exist.+isOlderThan :: FilePath -> [TimedFile] -> IO Bool+isOlderThan cache files = do+ exist <- doesFileExist cache+ if not exist+ then return True+ else do+ tCache <- getModificationTime cache+ return $ any (tCache <=) $ map tfTime files -- including equal just in case - return $ collectModules- $ extractBindings `concatMap` (moduleInfos `zip` modules)+-- | Browsing all functions in all system modules.+getGlobalSymbolTable :: LightGhc [(Symbol, [ModuleString])]+getGlobalSymbolTable = do+ df <- G.getSessionDynFlags+ let mods = listVisibleModules df+ moduleInfos <- mapM G.getModuleInfo mods+ return $ collectModules+ $ extractBindings `concatMap` (moduleInfos `zip` mods) -extractBindings :: (Maybe G.ModuleInfo, ModuleString)+extractBindings :: (Maybe G.ModuleInfo, G.Module) -> [(Symbol, ModuleString)]-extractBindings (Nothing,_) = []-extractBindings (Just inf,mdlname) =- map (\name -> (getOccString name, mdlname)) names+extractBindings (Nothing, _) = []+extractBindings (Just inf, mdl) =+ map (\name -> (getOccString name, modStr)) names where- names = G.modInfoExports inf+ names = G.modInfoExports inf+ modStr = ModuleString $ moduleNameString $ moduleName mdl -collectModules :: [(Symbol,ModuleString)]- -> [(Symbol,[ModuleString])]+collectModules :: [(Symbol, ModuleString)]+ -> [(Symbol, [ModuleString])] collectModules = map tieup . groupBy ((==) `on` fst) . sort where tieup x = (head (map fst x), map snd x)
− Language/Haskell/GhcMod/GHCApi.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.GhcMod.GHCApi (- ghcPkgDb- , package- , modules- , findModule- , moduleInfo- , localModuleInfo- , bindings- ) where--import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.Monad (GhcModT)-import Language.Haskell.GhcMod.Target (setTargetFiles)-import Language.Haskell.GhcMod.Types--import Control.Applicative ((<$>))-import Distribution.Package (InstalledPackageId(..))-import qualified Data.Map as M-import GHC (DynFlags(..))-import qualified GHC as G-import GhcMonad-import qualified Packages as G-import qualified Module as G-import qualified OccName as G--------------------------------------------------------------------- get Packages,Modules,Bindings--ghcPkgDb :: GhcMonad m => m PkgDb-ghcPkgDb = M.fromList <$>- maybe [] (map toKv . filterInternal) <$> pkgDatabase <$> G.getSessionDynFlags- where- toKv pkg = (fromInstalledPackageId $ G.installedPackageId pkg, pkg)- filterInternal =- filter ((/= InstalledPackageId "builtin_rts") . G.installedPackageId)--package :: G.PackageConfig -> Package-package = fromInstalledPackageId . G.installedPackageId--modules :: G.PackageConfig -> [ModuleString]-modules = map G.moduleNameString . G.exposedModules--findModule :: ModuleString -> PkgDb -> [Package]-findModule m db = M.elems $ package `M.map` (containsModule `M.filter` db)- where- containsModule :: G.PackageConfig -> Bool- containsModule pkgConf =- G.mkModuleName m `elem` G.exposedModules pkgConf---ghcPkgId :: Package -> G.PackageId-ghcPkgId (name,_,_) =- -- TODO: Adding the package version too breaks 'findModule' for some reason- -- this isn't a big deal since in the common case where we're in a cabal- -- project we just use cabal's view of package dependencies anyways so we're- -- guaranteed to only have one version of each package exposed. However when- -- we're operating without a cabal project this will probaly cause trouble.- G.stringToPackageId name--type Binding = String---- | @moduleInfo mpkg module@. @mpkg@ should be 'Nothing' iff. moduleInfo--- should look for @module@ in the working directory.------ To map a 'ModuleString' to a package see 'findModule'-moduleInfo :: IOish m- => Maybe Package- -> ModuleString- -> GhcModT m (Maybe G.ModuleInfo)-moduleInfo mpkg mdl = do- let mdlName = G.mkModuleName mdl- mfsPkgId = G.packageIdFS . ghcPkgId <$> mpkg- loadLocalModule- G.findModule mdlName mfsPkgId >>= G.getModuleInfo- where- loadLocalModule = case mpkg of- Just _ -> return ()- Nothing -> setTargetFiles [mdl]--localModuleInfo :: IOish m => ModuleString -> GhcModT m (Maybe G.ModuleInfo)-localModuleInfo mdl = moduleInfo Nothing mdl--bindings :: G.ModuleInfo -> [Binding]-bindings minfo = map (G.occNameString . G.getOccName) $ G.modInfoExports minfo
− Language/Haskell/GhcMod/GHCChoice.hs
@@ -1,23 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module Language.Haskell.GhcMod.GHCChoice where--import Control.Exception (IOException)-import CoreMonad (liftIO)-import qualified Exception as GE-import GHC (GhcMonad)---------------------------------------------------------------------- | Try the left 'Ghc' action. If 'IOException' occurs, try--- the right 'Ghc' action.-(||>) :: GhcMonad m => m a -> m a -> m a-x ||> y = x `GE.gcatch` (\(_ :: IOException) -> y)---- | Go to the next 'Ghc' monad by throwing 'AltGhcgoNext'.-goNext :: GhcMonad m => m a-goNext = liftIO . GE.throwIO $ userError "goNext"---- | Run any one 'Ghc' monad.-runAnyOne :: GhcMonad m => [m a] -> m a-runAnyOne = foldr (||>) goNext
Language/Haskell/GhcMod/Gap.hs view
@@ -13,7 +13,6 @@ , showSeverityCaption , setCabalPkg , setHideAllPackages- , addPackageFlags , setDeferTypeErrors , setWarnTypedHoles , setDumpSplices@@ -33,14 +32,15 @@ , fileModSummary , WarnFlags , emptyWarnFlags- , benchmarkBuildInfo- , benchmarkTargets- , toModuleString , GLMatch , GLMatchI , getClass , occName- , setFlags+ , listVisibleModuleNames+ , listVisibleModules+ , lookupModulePackageInAllPackages+ , Language.Haskell.GhcMod.Gap.isSynTyCon+ , parseModuleHeader ) where import Control.Applicative hiding (empty)@@ -49,15 +49,15 @@ import Data.List (intersperse) import Data.Maybe (catMaybes) import Data.Time.Clock (UTCTime)+import Data.Traversable hiding (mapM) import DataCon (dataConRepType) import Desugar (deSugarExpr) import DynFlags import ErrUtils+import Exception import FastString import GhcMonad import HscTypes-import Language.Haskell.GhcMod.GHCChoice-import Language.Haskell.GhcMod.Types import NameSet import OccName import Outputable@@ -65,8 +65,8 @@ import StringBuffer import TcType import Var (varType)+import System.Directory -import qualified Distribution.PackageDescription as P import qualified InstEnv import qualified Pretty import qualified StringBuffer as SB@@ -88,11 +88,24 @@ import RdrName (rdrNameOcc) #endif +#if __GLASGOW_HASKELL__ < 710+import UniqFM (eltsUFM)+import Module+#endif+ #if __GLASGOW_HASKELL__ >= 704 import qualified Data.IntSet as I (IntSet, empty)-import qualified Distribution.ModuleName as M (ModuleName,toFilePath) #endif +import Bag+import Lexer as L+import Parser+import SrcLoc+import Packages++import Language.Haskell.GhcMod.Types (Expression(..))+import Prelude+ ---------------------------------------------------------------- ---------------------------------------------------------------- --@@ -173,7 +186,11 @@ ---------------------------------------------------------------- fOptions :: [String]-#if __GLASGOW_HASKELL__ >= 704+#if __GLASGOW_HASKELL__ >= 710+fOptions = [option | (FlagSpec option _ _ _) <- fFlags]+ ++ [option | (FlagSpec option _ _ _) <- fWarningFlags]+ ++ [option | (FlagSpec option _ _ _) <- fLangFlags]+#elif __GLASGOW_HASKELL__ >= 704 fOptions = [option | (option,_,_) <- fFlags] ++ [option | (option,_,_) <- fWarningFlags] ++ [option | (option,_,_) <- fLangFlags]@@ -187,9 +204,11 @@ ---------------------------------------------------------------- fileModSummary :: GhcMonad m => FilePath -> m ModSummary-fileModSummary file = do+fileModSummary file' = do mss <- getModuleGraph- let [ms] = filter (\m -> ml_hs_file (ms_location m) == Just file) mss+ file <- liftIO $ canonicalizePath file'+ [ms] <- liftIO $ flip filterM mss $ \m ->+ (Just file==) <$> canonicalizePath `traverse` ml_hs_file (ms_location m) return ms withContext :: GhcMonad m => m a -> m a@@ -202,26 +221,31 @@ action topImports = do mss <- getModuleGraph- ms <- map modName <$> filterM isTop mss+ mns <- map modName <$> filterM isTop mss+ let ii = map IIModule mns #if __GLASGOW_HASKELL__ >= 704- return ms+ return ii #else- return (ms,[])+ return (ii,[]) #endif isTop mos = lookupMod mos ||> returnFalse lookupMod mos = lookupModule (ms_mod_name mos) Nothing >> return True returnFalse = return False #if __GLASGOW_HASKELL__ >= 706- modName = IIModule . moduleName . ms_mod+ modName = moduleName . ms_mod setCtx = setContext #elif __GLASGOW_HASKELL__ >= 704- modName = IIModule . ms_mod+ modName = ms_mod setCtx = setContext #else modName = ms_mod setCtx = uncurry setContext #endif +-- | Try the left action, if an IOException occurs try the right action.+(||>) :: ExceptionMonad m => m a -> m a -> m a+x ||> y = x `gcatch` (\(_ :: IOException) -> y)+ showSeverityCaption :: Severity -> String #if __GLASGOW_HASKELL__ >= 706 showSeverityCaption SevWarning = "Warning: "@@ -249,12 +273,6 @@ setHideAllPackages df = dopt_set df Opt_HideAllPackages #endif -addPackageFlags :: [Package] -> DynFlags -> DynFlags-addPackageFlags pkgs df =- df { packageFlags = packageFlags df ++ expose `map` pkgs }- where- expose pkg = ExposePackageId $ showPkgId pkg- ---------------------------------------------------------------- setDumpSplices :: DynFlags -> DynFlags@@ -310,8 +328,8 @@ where implicits = mkNameSet [getName t | x <- xs, t <- implicitTyThings (get_thing x)] -infoThing :: GhcMonad m => String -> m SDoc-infoThing str = do+infoThing :: GhcMonad m => Expression -> m SDoc+infoThing (Expression str) = do names <- parseName str #if __GLASGOW_HASKELL__ >= 708 mb_stuffs <- mapM (getInfo False) names@@ -413,29 +431,6 @@ ---------------------------------------------------------------- ---------------------------------------------------------------- -benchmarkBuildInfo :: P.PackageDescription -> [P.BuildInfo]-#if __GLASGOW_HASKELL__ >= 704-benchmarkBuildInfo pd = map P.benchmarkBuildInfo $ P.benchmarks pd-#else-benchmarkBuildInfo pd = []-#endif--benchmarkTargets :: P.PackageDescription -> [String]-#if __GLASGOW_HASKELL__ >= 704-benchmarkTargets pd = map toModuleString $ concatMap P.benchmarkModules $ P.benchmarks pd-#else-benchmarkTargets = []-#endif--toModuleString :: M.ModuleName -> String-toModuleString mn = fromFilePath $ M.toFilePath mn- where- fromFilePath :: FilePath -> String- fromFilePath fp = map (\c -> if c=='/' then '.' else c) fp------------------------------------------------------------------------------------------------------------------------------------ #if __GLASGOW_HASKELL__ >= 708 type GLMatch = LMatch RdrName (LHsExpr RdrName) type GLMatchI = LMatch Id (LHsExpr Id)@@ -445,8 +440,13 @@ #endif getClass :: [LInstDecl Name] -> Maybe (Name, SrcSpan)-#if __GLASGOW_HASKELL__ >= 708+#if __GLASGOW_HASKELL__ >= 710 -- Instance declarations of sort 'instance F (G a)'+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsForAllTy _ _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _))))}))] = Just (className, loc)+-- Instance declarations of sort 'instance F G' (no variables)+getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsAppTy (L _ (HsTyVar className)) _))}))] = Just (className, loc)+#elif __GLASGOW_HASKELL__ >= 708+-- Instance declarations of sort 'instance F (G a)' getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _))))}))] = Just (className, loc) -- Instance declarations of sort 'instance F G' (no variables) getClass [L loc (ClsInstD (ClsInstDecl {cid_poly_ty = (L _ (HsAppTy (L _ (HsTyVar className)) _))}))] = Just (className, loc)@@ -465,11 +465,73 @@ #endif --------------------------------------------------------------------------------------------------------------------------------- -setFlags :: DynFlags -> DynFlags-#if __GLASGOW_HASKELL__ >= 708-setFlags df = df `gopt_unset` Opt_SpecConstr -- consume memory if -O2+#if __GLASGOW_HASKELL__ < 710+-- Copied from ghc/InteractiveUI.hs+allExposedPackageConfigs :: DynFlags -> [PackageConfig]+allExposedPackageConfigs df = filter exposed $ eltsUFM $ pkgIdMap $ pkgState df++allExposedModules :: DynFlags -> [ModuleName]+allExposedModules df = concat $ map exposedModules $ allExposedPackageConfigs df++listVisibleModuleNames :: DynFlags -> [ModuleName]+listVisibleModuleNames = allExposedModules+#endif++lookupModulePackageInAllPackages ::+ DynFlags -> ModuleName -> [String]+lookupModulePackageInAllPackages df mn =+#if __GLASGOW_HASKELL__ >= 710+ unpackSPId . sourcePackageId . snd <$> lookupModuleInAllPackages df mn+ where+ unpackSPId (SourcePackageId fs) = unpackFS fs #else-setFlags = id+ unpackPId . sourcePackageId . fst <$> lookupModuleInAllPackages df mn+ where+ unpackPId pid = packageIdString $ mkPackageId pid+-- n ++ "-" ++ showVersion v #endif++listVisibleModules :: DynFlags -> [GHC.Module]+listVisibleModules df = let+#if __GLASGOW_HASKELL__ >= 710+ modNames = listVisibleModuleNames df+ mods = [ m | mn <- modNames, (m, _) <- lookupModuleInAllPackages df mn ]+#else+ pkgCfgs = allExposedPackageConfigs df+ mods = [ mkModule pid modname | p <- pkgCfgs+ , let pid = packageConfigId p+ , modname <- exposedModules p ]+#endif+ in mods++isSynTyCon :: TyCon -> Bool+#if __GLASGOW_HASKELL__ >= 710+isSynTyCon = GHC.isTypeSynonymTyCon+#else+isSynTyCon = GHC.isSynTyCon+#endif+++parseModuleHeader+ :: String -- ^ Haskell module source text (full Unicode is supported)+ -> DynFlags+ -> FilePath -- ^ the filename (for source locations)+ -> Either ErrorMessages (WarningMessages, Located (HsModule RdrName))+parseModuleHeader str dflags filename =+ let+ loc = mkRealSrcLoc (mkFastString filename) 1 1+ buf = stringToStringBuffer str+ in+ case L.unP Parser.parseHeader (mkPState dflags buf loc) of++ PFailed sp err ->+#if __GLASGOW_HASKELL__ >= 706+ Left (unitBag (mkPlainErrMsg dflags sp err))+#else+ Left (unitBag (mkPlainErrMsg sp err))+#endif++ POk pst rdr_module ->+ let (warns,_) = getMessages pst in+ Right (warns, rdr_module)
Language/Haskell/GhcMod/GhcPkg.hs view
@@ -4,55 +4,29 @@ , ghcPkgDbStackOpts , ghcDbStackOpts , ghcDbOpt- , fromInstalledPackageId- , fromInstalledPackageId' , getPackageDbStack , getPackageCachePaths ) where import Config (cProjectVersion, cTargetPlatformString, cProjectVersionInt)-import Control.Applicative ((<$>))-import Data.List (intercalate)+import Control.Applicative import Data.List.Split (splitOn) import Data.Maybe-import Distribution.Package (InstalledPackageId(..)) import Exception (handleIO)-import Language.Haskell.GhcMod.PathsAndFiles-import Language.Haskell.GhcMod.Types import System.Directory (doesDirectoryExist, getAppUserDataDirectory) import System.FilePath ((</>))+import Prelude +import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.CabalHelper+import Language.Haskell.GhcMod.PathsAndFiles+ ghcVersion :: Int ghcVersion = read cProjectVersionInt -getPackageDbStack :: FilePath -- ^ Project Directory (where the- -- cabal.sandbox.config file would be if it- -- exists)- -> IO [GhcPkgDb]-getPackageDbStack cdir = do- mSDir <- getSandboxDb cdir- return $ [GlobalDb] ++ case mSDir of- Nothing -> [UserDb]- Just db -> [PackageDb db]- ---------------------------------------------------------------- -fromInstalledPackageId' :: InstalledPackageId -> Maybe Package-fromInstalledPackageId' pid = let- InstalledPackageId pkg = pid- in case reverse $ splitOn "-" pkg of- i:v:rest -> Just (intercalate "-" (reverse rest), v, i)- _ -> Nothing--fromInstalledPackageId :: InstalledPackageId -> Package-fromInstalledPackageId pid =- case fromInstalledPackageId' pid of- Just p -> p- Nothing -> error $- "fromInstalledPackageId: `"++show pid++"' is not a valid package-id"------------------------------------------------------------------- -- | Get options needed to add a list of package dbs to ghc-pkg's db stack ghcPkgDbStackOpts :: [GhcPkgDb] -- ^ Package db stack -> [String]@@ -85,11 +59,24 @@ ---------------------------------------------------------------- --getPackageCachePaths :: FilePath -> Cradle -> IO [FilePath]-getPackageCachePaths sysPkgCfg crdl =- catMaybes <$> resolvePackageConfig sysPkgCfg `mapM` cradlePkgDbStack crdl+getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb]+getPackageDbStack = do+ crdl <- cradle+ mCusPkgStack <- getCustomPkgDbStack+ stack <- case cradleProjectType crdl of+ PlainProject ->+ return [GlobalDb, UserDb]+ SandboxProject -> do+ Just db <- liftIO $ getSandboxDb $ cradleRootDir crdl+ return $ [GlobalDb, db]+ CabalProject ->+ getCabalPackageDbStack+ return $ fromMaybe stack mCusPkgStack +getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath]+getPackageCachePaths sysPkgCfg = do+ pkgDbStack <- getPackageDbStack+ catMaybes <$> (liftIO . resolvePackageConfig sysPkgCfg) `mapM` pkgDbStack -- TODO: use PkgConfRef --- Copied from ghc module `Packages' unfortunately it's not exported :/
+ Language/Haskell/GhcMod/HomeModuleGraph.hs view
@@ -0,0 +1,263 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE ScopedTypeVariables, RecordWildCards #-}+module Language.Haskell.GhcMod.HomeModuleGraph (+ GmModuleGraph(..)+ , ModulePath(..)+ , mkFileMap+ , mkModuleMap+ , mkMainModulePath+ , findModulePath+ , findModulePathSet+ , fileModuleName+ , canonicalizeModulePath+ , homeModuleGraph+ , updateHomeModuleGraph+ , canonicalizeModuleGraph+ , reachable+ , moduleGraphToDot+ ) where++import DriverPipeline+import DynFlags+import ErrUtils+import Exception+import Finder+import GHC+import HscTypes++import Control.Arrow ((&&&))+import Control.Applicative+import Control.Monad+import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)+import Control.Monad.State.Strict (execStateT)+import Control.Monad.State.Class+import Data.Maybe+import Data.Monoid as Monoid+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import System.FilePath+import System.Directory+import Prelude++import Language.Haskell.GhcMod.Logging+import Language.Haskell.GhcMod.Logger+import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Gap (parseModuleHeader)++-- | Turn module graph into a graphviz dot file+--+-- @dot -Tpng -o modules.png modules.dot@+moduleGraphToDot :: GmModuleGraph -> String+moduleGraphToDot GmModuleGraph { gmgGraph } =+ "digraph {\n" ++ concatMap edges (Map.toList graph) ++ "}\n"+ where+ graph = Map.map (Set.mapMonotonic mpPath)+ $ Map.mapKeysMonotonic mpPath gmgGraph+ edges :: (FilePath, (Set FilePath)) -> String+ edges (f, sf) =+ concatMap (\f' -> " \""++ f ++"\" -> \""++ f' ++"\"\n") (Set.toList sf)++data S = S {+ sErrors :: [(ModulePath, ErrorMessages)],+ sWarnings :: [(ModulePath, WarningMessages)],+ sGraph :: GmModuleGraph+}++defaultS :: S+defaultS = S [] [] mempty++putErr :: MonadState S m+ => (ModulePath, ErrorMessages) -> m ()+putErr e = do+ s <- get+ put s { sErrors = e:sErrors s}++putWarn :: MonadState S m+ => (ModulePath, ErrorMessages) -> m ()+putWarn w = do+ s <- get+ put s { sWarnings = w:sWarnings s}++gmgLookupMP :: MonadState S m => ModulePath -> m (Maybe (Set ModulePath))+gmgLookupMP k = (Map.lookup k . gmgGraph . sGraph) `liftM` get++graphUnion :: MonadState S m => GmModuleGraph -> m ()+graphUnion gmg = do+ s <- get+ put s { sGraph = sGraph s `mappend` gmg }++reachable :: Set ModulePath -> GmModuleGraph -> Set ModulePath+reachable smp0 GmModuleGraph {..} = go smp0+ where+ go smp = let+ δsmp = Set.unions $+ collapseMaybeSet . flip Map.lookup gmgGraph <$> Set.toList smp+ smp' = smp `Set.union` δsmp+ in if smp == smp' then smp' else go smp'++pruneUnreachable :: Set ModulePath -> GmModuleGraph -> GmModuleGraph+pruneUnreachable smp0 gmg@GmModuleGraph {..} = let+ r = reachable smp0 gmg+ in+ GmModuleGraph {+ gmgGraph = Map.filterWithKey (\k _ -> k `Set.member` r) gmgGraph+ }++collapseMaybeSet :: Maybe (Set a) -> Set a+collapseMaybeSet = maybe Set.empty id++homeModuleGraph :: (IOish m, GmLog m, GmEnv m)+ => HscEnv -> Set ModulePath -> m GmModuleGraph+homeModuleGraph env smp = updateHomeModuleGraph env mempty smp smp++mkMainModulePath :: FilePath -> ModulePath+mkMainModulePath = ModulePath (mkModuleName "Main")++findModulePath :: HscEnv -> ModuleName -> IO (Maybe ModulePath)+findModulePath env mn = do+ fmap (ModulePath mn) <$> find env mn++findModulePathSet :: HscEnv -> [ModuleName] -> IO (Set ModulePath)+findModulePathSet env mns = do+ Set.fromList . catMaybes <$> findModulePath env `mapM` mns++find :: MonadIO m => HscEnv -> ModuleName -> m (Maybe FilePath)+find env mn = liftIO $ do+ res <- findHomeModule env mn+ case res of+ -- TODO: handle SOURCE imports (hs-boot stuff): addBootSuffixLocn loc+ Found loc@ModLocation { ml_hs_file = Just _ } _mod ->+ return $ normalise <$> ml_hs_file loc+ _ -> return Nothing+++canonicalizeModulePath :: ModulePath -> IO ModulePath+canonicalizeModulePath (ModulePath mn fp) = ModulePath mn <$> canonicalizePath fp++canonicalizeModuleGraph :: MonadIO m => GmModuleGraph -> m GmModuleGraph+canonicalizeModuleGraph GmModuleGraph {..} = liftIO $ do+ GmModuleGraph . Map.fromList <$> mapM fmg (Map.toList gmgGraph)+ where+ fmg (mp, smp) = liftM2 (,) (canonicalizeModulePath mp) (Set.fromList <$> mapM canonicalizeModulePath (Set.toList smp))+++updateHomeModuleGraph :: (IOish m, GmLog m, GmEnv m)+ => HscEnv+ -> GmModuleGraph+ -> Set ModulePath -- ^ Initial set of modules+ -> Set ModulePath -- ^ Updated set of modules+ -> m GmModuleGraph+updateHomeModuleGraph env GmModuleGraph {..} smp sump = do+ -- TODO: It would be good if we could retain information about modules that+ -- stop to compile after we've already successfully parsed them at some+ -- point. Figure out a way to delete the modules about to be updated only+ -- after we're sure they won't fail to parse .. or something. Should probably+ -- push this whole prune logic deep into updateHomeModuleGraph'+ (pruneUnreachable smp . sGraph) `liftM` runS (updateHomeModuleGraph' env sump)+ where+ runS = flip execStateT defaultS { sGraph = graph' }+ graph' = GmModuleGraph {+ gmgGraph = Set.foldr Map.delete gmgGraph sump+ }++mkFileMap :: Set ModulePath -> Map FilePath ModulePath+mkFileMap smp = Map.fromList $ map (mpPath &&& id) $ Set.toList smp++mkModuleMap :: Set ModulePath -> Map ModuleName ModulePath+mkModuleMap smp = Map.fromList $ map (mpModule &&& id) $ Set.toList smp++updateHomeModuleGraph'+ :: forall m. (MonadState S m, IOish m, GmLog m, GmEnv m)+ => HscEnv+ -> Set ModulePath -- ^ Initial set of modules+ -> m ()+updateHomeModuleGraph' env smp0 = do+ go `mapM_` Set.toList smp0+ where+ go :: ModulePath -> m ()+ go mp = do+ msmp <- gmgLookupMP mp+ case msmp of+ Just _ -> return ()+ Nothing -> do+ smp <- collapseMaybeSet `liftM` step mp++ graphUnion GmModuleGraph {+ gmgGraph = Map.singleton mp smp+ }++ mapM_ go (Set.toList smp)++ step :: ModulePath -> m (Maybe (Set ModulePath))+ step mp = runMaybeT $ do+ (dflags, ppsrc_fn) <- MaybeT preprocess'+ src <- liftIO $ readFile ppsrc_fn+ imports mp src dflags+ where+ preprocess' :: m (Maybe (DynFlags, FilePath))+ preprocess' = do+ let fn = mpPath mp+ ep <- preprocessFile env fn+ case ep of+ Right (_, x) -> return $ Just x+ Left errs -> do+ -- TODO: Remember these and present them as proper errors if this is+ -- the file the user is looking at.+ gmLog GmWarning ("preprocess " ++ show fn) $ Monoid.mempty $+$ (vcat $ map text errs)+ return Nothing++ imports :: ModulePath -> String -> DynFlags -> MaybeT m (Set ModulePath)+ imports mp@ModulePath {..} src dflags =+ case parseModuleHeader src dflags mpPath of+ Left err -> do+ putErr (mp, err)+ mzero++ Right (ws, lmdl) -> do+ putWarn (mp, ws)+ let HsModule {..} = unLoc lmdl+ mns = map (unLoc . ideclName)+ $ filter (isNothing . ideclPkgQual)+ $ map unLoc hsmodImports+ liftIO $ Set.fromList . catMaybes <$> mapM (findModulePath env) mns++preprocessFile :: MonadIO m =>+ HscEnv -> FilePath -> m (Either [String] ([String], (DynFlags, FilePath)))+preprocessFile env file =+ liftIO $ withLogger' env $ \setDf -> do+ let env' = env { hsc_dflags = setDf (hsc_dflags env) }+ preprocess env' (file, Nothing)++fileModuleName ::+ HscEnv -> FilePath -> IO (Either [String] (Maybe ModuleName))+fileModuleName env fn = handle (\(_ :: SomeException) -> return $ Right Nothing) $ do+ ep <- preprocessFile env fn+ case ep of+ Left errs -> do+ return $ Left errs+ Right (_warns, (dflags, procdFile)) -> do+ src <- readFile procdFile+ case parseModuleHeader src dflags procdFile of+ Left errs -> do+ return $ Left $ errBagToStrList env errs+ Right (_, lmdl) -> do+ let HsModule {..} = unLoc lmdl+ return $ Right $ unLoc <$> hsmodName
Language/Haskell/GhcMod/Info.hs view
@@ -3,20 +3,25 @@ , types ) where -import Control.Applicative ((<$>))+import Control.Applicative import Data.Function (on) import Data.List (sortBy) import Data.Maybe (catMaybes)+import System.FilePath import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, LHsBind, LHsExpr, LPat, Id, TypecheckedModule(..), SrcSpan, Type)+import Prelude import qualified GHC as G-import Language.Haskell.GhcMod.Doc (showPage)-import Language.Haskell.GhcMod.Gap (HasType(..)) import qualified Language.Haskell.GhcMod.Gap as Gap++import Language.Haskell.GhcMod.Convert+import Language.Haskell.GhcMod.Doc+import Language.Haskell.GhcMod.DynFlags+import Language.Haskell.GhcMod.Gap+import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.Convert ---------------------------------------------------------------- @@ -25,15 +30,23 @@ => FilePath -- ^ A target file. -> Expression -- ^ A Haskell expression. -> GhcModT m String-info file expr = do- opt <- options- convert opt <$> ghandle handler body+info file expr =+ ghandle handler $+ runGmlT' [Left file] deferErrors $+ withContext $+ convert <$> options <*> body where- body = inModuleContext file $ \dflag style -> do- sdoc <- Gap.infoThing expr- return $ showPage dflag style sdoc- handler (SomeException _) = return "Cannot show info"+ handler (SomeException ex) = do+ gmLog GmException "info" $ text "" $$ nest 4 (showDoc ex)+ convert' "Cannot show info" + body :: GhcMonad m => m String+ body = do+ sdoc <- Gap.infoThing expr+ st <- getStyle+ dflag <- G.getSessionDynFlags+ return $ showPage dflag st sdoc+ ---------------------------------------------------------------- -- | Obtaining type of a target expression. (GHCi's type:)@@ -42,24 +55,29 @@ -> Int -- ^ Line number. -> Int -- ^ Column number. -> GhcModT m String-types file lineNo colNo = do- opt <- options- convert opt <$> ghandle handler body- where- body = inModuleContext file $ \dflag style -> do- modSum <- Gap.fileModSummary file+types file lineNo colNo =+ ghandle handler $+ runGmlT' [Left file] deferErrors $+ withContext $ do+ crdl <- cradle+ modSum <- Gap.fileModSummary (cradleCurrentDir crdl </> file) srcSpanTypes <- getSrcSpanType modSum lineNo colNo- return $ map (toTup dflag style) $ sortBy (cmp `on` fst) srcSpanTypes- handler (SomeException _) = return []+ dflag <- G.getSessionDynFlags+ st <- getStyle+ convert' $ map (toTup dflag st) $ sortBy (cmp `on` fst) srcSpanTypes+ where+ handler (SomeException ex) = do+ gmLog GmException "types" $ showDoc ex+ return [] getSrcSpanType :: GhcMonad m => G.ModSummary -> Int -> Int -> m [(SrcSpan, Type)] getSrcSpanType modSum lineNo colNo = do- p <- G.parseModule modSum- tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p- let bs = listifySpans tcs (lineNo, colNo) :: [LHsBind Id]- es = listifySpans tcs (lineNo, colNo) :: [LHsExpr Id]- ps = listifySpans tcs (lineNo, colNo) :: [LPat Id]- bts <- mapM (getType tcm) bs- ets <- mapM (getType tcm) es- pts <- mapM (getType tcm) ps- return $ catMaybes $ concat [ets, bts, pts]+ p <- G.parseModule modSum+ tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p+ let bs = listifySpans tcs (lineNo, colNo) :: [LHsBind Id]+ es = listifySpans tcs (lineNo, colNo) :: [LHsExpr Id]+ ps = listifySpans tcs (lineNo, colNo) :: [LPat Id]+ bts <- mapM (getType tcm) bs+ ets <- mapM (getType tcm) es+ pts <- mapM (getType tcm) ps+ return $ catMaybes $ concat [ets, bts, pts]
Language/Haskell/GhcMod/Internal.hs view
@@ -8,35 +8,33 @@ , PackageVersion , PackageId , IncludeDir- , CompilerOptions(..)- -- * Cabal API- , parseCabalFile- , getCompilerOptions- , cabalAllBuildInfo- , cabalDependPackages- , cabalSourceDirs- , cabalAllTargets+ , GmlT(..)+ , MonadIO(..)+ , GmEnv(..) -- * Various Paths , ghcLibDir , ghcModExecutable- -- * IO- , getDynamicFlags- -- * Targets- , setTargetFiles -- * Logging , withLogger , setNoWarningFlags , setAllWarningFlags -- * Environment, state and logging , GhcModEnv(..)- , newGhcModEnv , GhcModState- , defaultState , CompilerMode(..) , GhcModLog+ , GmLog(..)+ , GmLogLevel(..)+ , gmSetLogLevel -- * Monad utilities , runGhcModT' , hoistGhcModT+ , runGmlT+ , runGmlT'+ , gmlGetSession+ , gmlSetSession+ , loadTargets+ , cabalResolvedComponents -- ** Accessing 'GhcModEnv' and 'GhcModState' , options , cradle@@ -45,28 +43,33 @@ , withOptions -- * 'GhcModError' , gmeDoc- -- * 'GhcMonad' Choice- , (||>)- , goNext- , runAnyOne -- * World , World , getCurrentWorld , didWorldChange+ -- * Cabal Helper+ , ModulePath(..)+ , GmComponent(..)+ , GmComponentType(..)+ , GmModuleGraph(..)+ , prepareCabalHelper+ -- * Misc stuff+ , GHandler(..)+ , gcatches ) where import GHC.Paths (libdir) -import Language.Haskell.GhcMod.CabalApi+import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.Error-import Language.Haskell.GhcMod.GHCChoice import Language.Haskell.GhcMod.Logger+import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils import Language.Haskell.GhcMod.World+import Language.Haskell.GhcMod.CabalHelper -- | Obtaining the directory for ghc system libraries. ghcLibDir :: FilePath
Language/Haskell/GhcMod/Logger.hs view
@@ -1,31 +1,33 @@-{-# LANGUAGE CPP #-}- module Language.Haskell.GhcMod.Logger ( withLogger- , withLoggerTwice+ , withLogger' , checkErrorPrefix+ , errsToStr+ , errBagToStrList ) where -import Bag (Bag, bagToList, emptyBag, consBag, filterBag, unionBags)-import Control.Applicative ((<$>))+import Control.Arrow+import Control.Applicative+import Data.List (isPrefixOf)+import Data.Maybe (fromMaybe) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)-import Data.List (isPrefixOf, find, nub, isInfixOf)-import Data.Maybe (fromMaybe, isJust)-import ErrUtils (ErrMsg, WarnMsg, errMsgShortDoc, errMsgExtraInfo, mkWarnMsg)-import Exception (ghandle)+import System.FilePath (normalise)+import Text.PrettyPrint++import ErrUtils (ErrMsg, errMsgShortDoc, errMsgExtraInfo) import GHC (DynFlags, SrcSpan, Severity(SevError))+import HscTypes+import Outputable import qualified GHC as G-import HscTypes (SourceError, srcErrorMessages)-import Language.Haskell.GhcMod.Doc (showPage, getStyle)-import Language.Haskell.GhcMod.DynFlags (withDynFlags, withCmdFlags)-import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Convert (convert')-import Language.Haskell.GhcMod.Monad-import Language.Haskell.GhcMod.Types-import Outputable (PprStyle, SDoc, qualName, qualModule, mkErrStyle, neverQualify)-import System.FilePath (normalise)+import Bag -----------------------------------------------------------------+import Language.Haskell.GhcMod.Convert+import Language.Haskell.GhcMod.Doc (showPage)+import Language.Haskell.GhcMod.DynFlags (withDynFlags)+import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.Error+import qualified Language.Haskell.GhcMod.Gap as Gap+import Prelude type Builder = [String] -> [String] @@ -39,178 +41,94 @@ newLogRef :: IO LogRef newLogRef = LogRef <$> newIORef emptyLog -readAndClearLogRef :: IOish m => LogRef -> GhcModT m String+readAndClearLogRef :: LogRef -> IO [String] readAndClearLogRef (LogRef ref) = do- Log _ b <- liftIO $ readIORef ref- liftIO $ writeIORef ref emptyLog- convert' (b [])+ Log _ b <- readIORef ref+ writeIORef ref emptyLog+ return $ b [] appendLogRef :: DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()-appendLogRef df (LogRef ref) _ sev src style msg = modifyIORef ref update+appendLogRef df (LogRef ref) _ sev src st msg = modifyIORef ref update where- l = ppMsg src sev df style msg+ l = ppMsg src sev df st msg update lg@(Log ls b) | l `elem` ls = lg | otherwise = Log (l:ls) (b . (l:)) ---------------------------------------------------------------- -data LogBag = LogBag (Bag WarnMsg)-newtype LogBagRef = LogBagRef (IORef LogBag)--emptyLogBag :: LogBag-emptyLogBag = LogBag emptyBag--newLogBagRef :: IO LogBagRef-newLogBagRef = LogBagRef <$> newIORef emptyLogBag--readAndClearLogBagRef :: IOish m => LogBagRef -> GhcModT m (Bag WarnMsg)-readAndClearLogBagRef (LogBagRef ref) = do- LogBag b <- liftIO $ readIORef ref- liftIO $ writeIORef ref emptyLogBag- return b--appendLogBagRef :: DynFlags -> LogBagRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()-appendLogBagRef df (LogBagRef ref) _ _ src style msg = modifyIORef ref update- where- qstyle = (qualName style, qualModule style)-#if __GLASGOW_HASKELL__ >= 706- warnMsg = mkWarnMsg df src qstyle msg-#else- warnMsg = mkWarnMsg src qstyle msg-#endif- warnBag = consBag warnMsg emptyBag- update (LogBag b) = let (b1,b2) = mergeErrors df style b warnBag- in LogBag $ b1 `unionBags` b2------------------------------------------------------------------- -- | Set the session flag (e.g. "-Wall" or "-w:") then -- executes a body. Logged messages are returned as 'String'. -- Right is success and Left is failure.-withLogger :: IOish m+withLogger :: (GmGhc m, GmEnv m) => (DynFlags -> DynFlags)- -> GhcModT m ()- -> GhcModT m (Either String String)-withLogger setDF body = ghandle sourceError $ do- logref <- liftIO newLogRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF) $- withCmdFlags wflags $ do- body- Right <$> readAndClearLogRef logref- where- setLogger logref df = Gap.setLogAction df $ appendLogRef df logref--withLoggerTwice :: IOish m- => (DynFlags -> DynFlags)- -> GhcModT m ()- -> (DynFlags -> DynFlags)- -> GhcModT m ()- -> GhcModT m (Either String String)-withLoggerTwice setDF1 body1 setDF2 body2 = do- err1 <- ghandle sourceErrorBag $ do- logref <- liftIO newLogBagRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF1) $- withCmdFlags wflags $ do- body1- Right <$> readAndClearLogBagRef logref- err2 <- ghandle sourceErrorBag $ do- logref <- liftIO newLogBagRef- wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcUserOptions <$> options- withDynFlags (setLogger logref . setDF2) $- withCmdFlags wflags $ do- body2- Right <$> readAndClearLogBagRef logref- -- Merge errors and warnings- dflags <- G.getSessionDynFlags- style <- getStyle- case (err1, err2) of- (Right b1, Right b2) -> do let (warn1,_) = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right emptyBag (warn1 `unionBags` b2)- (Left b1, Right b2) -> do let (err,warn) = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right err warn- (Right b1, Left b2) -> do let (err,warn) = mergeErrors dflags style b2 b1- errAndWarnBagToStr Right err warn- (Left b1, Left b2) -> do let (err1',err2') = mergeErrors dflags style b1 b2- errAndWarnBagToStr Right (err1' `unionBags` err2') emptyBag- where- setLogger logref df = Gap.setLogAction df $ appendLogBagRef df logref+ -> m a+ -> m (Either String (String, a))+withLogger f action = do+ env <- G.getSession+ opts <- options+ let conv = convert opts+ eres <- withLogger' env $ \setDf ->+ withDynFlags (f . setDf) action+ return $ either (Left . conv) (Right . first conv) eres -----------------------------------------------------------------+withLogger' :: IOish m+ => HscEnv -> ((DynFlags -> DynFlags) -> m a) -> m (Either [String] ([String], a))+withLogger' env action = do+ logref <- liftIO $ newLogRef --- | Converting 'SourceError' to 'String'.-sourceError :: IOish m => SourceError -> GhcModT m (Either String String)-sourceError err = errBagToStr (srcErrorMessages err)+ let dflags = hsc_dflags env+ pu = icPrintUnqual dflags (hsc_IC env)+ st = mkUserStyle pu AllTheWay -errBagToStr :: IOish m => Bag ErrMsg -> GhcModT m (Either String String)-errBagToStr = errBagToStr' Left+ fn df = setLogger logref df -errBagToStr' :: IOish m => (String -> a) -> Bag ErrMsg -> GhcModT m a-errBagToStr' f err = do- dflags <- G.getSessionDynFlags- style <- getStyle- ret <- convert' (errBagToStrList dflags style err)- return $ f ret+ a <- gcatches (Right <$> action fn) (handlers dflags st)+ ls <- liftIO $ readAndClearLogRef logref -errAndWarnBagToStr :: IOish m => (String -> a) -> Bag ErrMsg -> Bag WarnMsg -> GhcModT m a-errAndWarnBagToStr f err warn = do- dflags <- G.getSessionDynFlags- -- style <- toGhcModT getStyle-#if __GLASGOW_HASKELL__ >= 706- let style = mkErrStyle dflags neverQualify-#else- let style = mkErrStyle neverQualify-#endif- ret <- convert' $ nub (errBagToStrList dflags style err ++ warnBagToStrList dflags style warn)- return $ f ret+ return $ ((,) ls <$> a) -errBagToStrList :: DynFlags -> PprStyle -> Bag ErrMsg -> [String]-errBagToStrList dflag style = map (ppErrMsg dflag style) . reverse . bagToList+ where+ setLogger logref df = Gap.setLogAction df $ appendLogRef df logref+ handlers df st = [+ GHandler $ \ex -> return $ Left $ sourceError df st ex,+ GHandler $ \ex -> return $ Left [render $ ghcExceptionDoc ex]+ ] -warnBagToStrList :: DynFlags -> PprStyle -> Bag WarnMsg -> [String]-warnBagToStrList dflag style = map (ppWarnMsg dflag style) . reverse . bagToList+errBagToStrList :: HscEnv -> Bag ErrMsg -> [String]+errBagToStrList env errs = let+ dflags = hsc_dflags env+ pu = icPrintUnqual dflags (hsc_IC env)+ st = mkUserStyle pu AllTheWay+ in errsToStr dflags st $ bagToList errs -sourceErrorBag :: IOish m => SourceError -> GhcModT m (Either (Bag ErrMsg) (Bag WarnMsg))-sourceErrorBag err = return $ Left (srcErrorMessages err)+---------------------------------------------------------------- -mergeErrors :: DynFlags -> PprStyle -> Bag ErrMsg -> Bag ErrMsg -> (Bag ErrMsg, Bag ErrMsg)-mergeErrors dflag style b1 b2 =- let b1Msgs = map (\err1 -> let m = ppWarnMsg dflag style err1 in (m, head $ lines m))- (bagToList b1)- mustBeB2 = \err2 -> let msg2 = ppWarnMsg dflag style err2- line2 = head $ lines msg2- in not . isJust $ find (\(msg1, line1) -> msg1 == msg2 || (line1 == line2 && isHoleMsg line1)) b1Msgs- in (b1, filterBag mustBeB2 b2)+-- | Converting 'SourceError' to 'String'.+sourceError :: DynFlags -> PprStyle -> SourceError -> [String]+sourceError df st src_err = errsToStr df st $ reverse $ bagToList $ srcErrorMessages src_err -isHoleMsg :: String -> Bool-isHoleMsg = isInfixOf "Found hole"+errsToStr :: DynFlags -> PprStyle -> [ErrMsg] -> [String]+errsToStr df st = map (ppErrMsg df st) ---------------------------------------------------------------- ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String-ppErrMsg dflag style err = ppMsg spn SevError dflag style msg ++ (if null ext then "" else "\n" ++ ext)- where- spn = Gap.errorMsgSpan err- msg = errMsgShortDoc err- ext = showPage dflag style (errMsgExtraInfo err)--ppWarnMsg :: DynFlags -> PprStyle -> ErrMsg -> String-ppWarnMsg dflag style err = ppMsg spn G.SevWarning dflag style msg ++ (if null ext then "" else "\n" ++ ext)+ppErrMsg dflag st err =+ ppMsg spn SevError dflag st msg ++ (if null ext then "" else "\n" ++ ext) where spn = Gap.errorMsgSpan err msg = errMsgShortDoc err- ext = showPage dflag style (errMsgExtraInfo err)+ ext = showPage dflag st (errMsgExtraInfo err) ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String-ppMsg spn sev dflag style msg = prefix ++ cts+ppMsg spn sev dflag st msg = prefix ++ cts where- cts = showPage dflag style msg- prefix = ppMsgPrefix spn sev dflag style cts+ cts = showPage dflag st msg+ prefix = ppMsgPrefix spn sev dflag st cts ppMsgPrefix :: SrcSpan -> Severity-> DynFlags -> PprStyle -> String -> String-ppMsgPrefix spn sev dflag _style cts =+ppMsgPrefix spn sev dflag _st cts = let defaultPrefix | Gap.isDumpSplices dflag = "" | otherwise = checkErrorPrefix
+ Language/Haskell/GhcMod/Logging.hs view
@@ -0,0 +1,102 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Language.Haskell.GhcMod.Logging (+ module Language.Haskell.GhcMod.Logging+ , module Language.Haskell.GhcMod.Pretty+ , GmLogLevel(..)+ , module Text.PrettyPrint+ , module Data.Monoid+ ) where++import Control.Applicative hiding (empty)+import Control.Monad+import Control.Monad.Trans.Class+import Data.List+import Data.Char+import Data.Monoid+import Data.Maybe+import System.IO+import System.FilePath+import Text.PrettyPrint hiding (style, (<>))+import Prelude++import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Pretty+import Language.Haskell.GhcMod.Output++gmSetLogLevel :: GmLog m => GmLogLevel -> m ()+gmSetLogLevel level =+ gmlJournal $ GhcModLog (Just level) (Last Nothing) []++gmSetDumpLevel :: GmLog m => Bool -> m ()+gmSetDumpLevel level =+ gmlJournal $ GhcModLog Nothing (Last (Just level)) []+++increaseLogLevel :: GmLogLevel -> GmLogLevel+increaseLogLevel l | l == maxBound = l+increaseLogLevel l = succ l++decreaseLogLevel :: GmLogLevel -> GmLogLevel+decreaseLogLevel l | l == minBound = l+decreaseLogLevel l = pred l++-- |+-- >>> Just GmDebug <= Nothing+-- False+-- >>> Just GmException <= Just GmDebug+-- True+-- >>> Just GmDebug <= Just GmException+-- False+gmLog :: (MonadIO m, GmLog m, GmEnv m) => GmLogLevel -> String -> Doc -> m ()+gmLog level loc' doc = do+ GhcModLog { gmLogLevel = Just level' } <- gmlHistory++ let loc | loc' == "" = empty+ | otherwise = text loc' <+>: empty+ msgDoc = gmLogLevelDoc level <+>: sep [loc, doc]+ msg = dropWhileEnd isSpace $ gmRenderDoc msgDoc++ when (level <= level') $ gmErrStrLn msg++ gmlJournal (GhcModLog Nothing (Last Nothing) [(level, loc', msgDoc)])++gmVomit :: (MonadIO m, GmLog m, GmEnv m) => String -> Doc -> String -> m ()+gmVomit filename doc content = do+ gmLog GmVomit "" $ doc <+>: text content++ GhcModLog { gmLogVomitDump = Last mdump }+ <- gmlHistory++ dir <- cradleTempDir `liftM` cradle+ when (fromMaybe False mdump) $+ liftIO $ writeFile (dir </> filename) content+++newtype LogDiscardT m a = LogDiscardT { runLogDiscard :: m a }+ deriving (Functor, Applicative, Monad)++instance MonadTrans LogDiscardT where+ lift = LogDiscardT++instance Monad m => GmLog (LogDiscardT m) where+ gmlJournal = const $ return ()+ gmlHistory = return mempty+ gmlClear = return ()
Language/Haskell/GhcMod/Modules.hs view
@@ -1,32 +1,26 @@ module Language.Haskell.GhcMod.Modules (modules) where -import Control.Applicative ((<$>))-import Control.Exception (SomeException(..))-import Data.List (nub, sort)-import qualified GHC as G+import Control.Arrow+import Data.List import Language.Haskell.GhcMod.Convert-import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types-import Packages (pkgIdMap, exposedModules, sourcePackageId, display)-import UniqFM (eltsUFM)+import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.Gap ( listVisibleModuleNames+ , lookupModulePackageInAllPackages+ ) +import qualified GHC as G+ ---------------------------------------------------------------- -- | Listing installed modules.-modules :: IOish m => GhcModT m String+modules :: (IOish m, GmEnv m, GmState m, GmLog m) => m String modules = do- opt <- options- convert opt . arrange opt <$> (getModules `G.gcatch` handler)- where- getModules = getExposedModules <$> G.getSessionDynFlags- getExposedModules = concatMap exposedModules'- . eltsUFM . pkgIdMap . G.pkgState- exposedModules' p =- map G.moduleNameString (exposedModules p)- `zip`- repeat (display $ sourcePackageId p)- arrange opt = nub . sort . map (dropPkgs opt)- dropPkgs opt (name, pkg)- | detailed opt = name ++ " " ++ pkg- | otherwise = name- handler (SomeException _) = return []+ Options { detailed } <- options+ df <- runGmPkgGhc G.getSessionDynFlags+ let mns = listVisibleModuleNames df+ pmnss = map (first moduleNameString) $ zip mns (modulePkg df `map` mns)+ convert' $ nub [ if detailed then pkg ++ " " ++ mn else mn+ | (mn, pkgs) <- pmnss, pkg <- pkgs ]+ where+ modulePkg df = lookupModulePackageInAllPackages df
Language/Haskell/GhcMod/Monad.hs view
@@ -1,289 +1,100 @@-{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-}-{-# LANGUAGE TypeFamilies, UndecidableInstances, RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>. +{-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.Monad (- -- * Monad Types- GhcModT- , IOish- -- ** Environment, state and logging- , GhcModEnv(..)- , newGhcModEnv- , GhcModState(..)- , defaultState- , CompilerMode(..)- , GhcModLog- , GhcModError(..)- -- * Monad utilities- , runGhcModT+ runGhcModT , runGhcModT'+ , runGhcModT'' , hoistGhcModT- -- ** Accessing 'GhcModEnv' and 'GhcModState'- , gmsGet- , gmsPut- , options- , cradle- , getCompilerMode- , setCompilerMode- , withOptions- , withTempSession- , overrideGhcUserOptions- -- ** Re-exporting convenient stuff- , liftIO- , module Control.Monad.Reader.Class- , module Control.Monad.Journal.Class+ , runGmlT+ , runGmlT'+ , runGmlTWith+ , runGmPkgGhc+ , withGhcModEnv+ , withGhcModEnv'+ , module Language.Haskell.GhcMod.Monad.Types ) where -#if __GLASGOW_HASKELL__ < 708--- 'CoreMonad.MonadIO' and 'Control.Monad.IO.Class.MonadIO' are different--- classes before ghc 7.8-#define DIFFERENT_MONADIO 1---- RWST doen't have a MonadIO instance before ghc 7.8-#define MONADIO_INSTANCES 1-#endif-- import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Error+import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Cradle-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.GhcPkg-import Language.Haskell.GhcMod.CabalApi-import qualified Language.Haskell.GhcMod.Gap as Gap--import DynFlags-import GHC-import qualified GHC as G-import GHC.Paths (libdir)-import GhcMonad hiding (withTempSession)-#if __GLASGOW_HASKELL__ <= 702-import HscTypes-#endif---- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.--- RWST does not automatically become an instance of MonadIO.--- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.--- So, RWST automatically becomes an instance of MonadIO.-import MonadUtils--#if DIFFERENT_MONADIO-import Control.Monad.Trans.Class (lift)-import qualified Control.Monad.IO.Class-import Data.Monoid (Monoid)-#endif+import Language.Haskell.GhcMod.Target+import Language.Haskell.GhcMod.Output -import Control.Applicative (Alternative) import Control.Arrow (first)-import Control.Monad (MonadPlus, void)-#if !MIN_VERSION_monad_control(1,0,0)-import Control.Monad (liftM)-#endif-import Control.Monad.Base (MonadBase, liftBase)---- Monad transformer stuff-import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith,- control, liftBaseOp, liftBaseOp_)--import Control.Monad.Trans.Class-import Control.Monad.Reader.Class-import Control.Monad.Writer.Class (MonadWriter)-import Control.Monad.State.Class (MonadState(..))--import Control.Monad.Error (ErrorT, runErrorT)-import Control.Monad.Reader (ReaderT, runReaderT)-import Control.Monad.State.Strict (StateT, runStateT)-import Control.Monad.Trans.Journal (JournalT, runJournalT)-#ifdef MONADIO_INSTANCES-import Control.Monad.Trans.Maybe (MaybeT)-import Control.Monad.Error (Error(..))-#endif-import Control.Monad.Journal.Class--import Data.Maybe (isJust)-import Data.IORef (IORef, readIORef, writeIORef, newIORef)-import System.Directory (getCurrentDirectory)--------------------------------------------------------------------data GhcModEnv = GhcModEnv {- gmGhcSession :: !(IORef HscEnv)- , gmOptions :: Options- , gmCradle :: Cradle- }--type GhcModLog = ()--data GhcModState = GhcModState {- gmCompilerMode :: CompilerMode- } deriving (Eq,Show,Read)--data CompilerMode = Simple | Intelligent deriving (Eq,Show,Read)--defaultState :: GhcModState-defaultState = GhcModState Simple---------------------------------------------------------------------- | This is basically a newtype wrapper around 'StateT', 'ErrorT', 'JournalT'--- and 'ReaderT' with custom instances for 'GhcMonad' and it's constraints that--- means you can run (almost) all functions from the GHC API on top of 'GhcModT'--- transparently.------ The inner monad @m@ should have instances for 'MonadIO' and--- 'MonadBaseControl' 'IO', in the common case this is simply 'IO'. Most @mtl@--- monads already have 'MonadBaseControl' 'IO' instances, see the--- @monad-control@ package.-newtype GhcModT m a = GhcModT {- unGhcModT :: StateT GhcModState- (ErrorT GhcModError- (JournalT GhcModLog- (ReaderT GhcModEnv m) ) ) a- } deriving ( Functor- , Applicative- , Alternative- , Monad- , MonadPlus-#if DIFFERENT_MONADIO- , Control.Monad.IO.Class.MonadIO-#endif- , MonadReader GhcModEnv -- TODO: make MonadReader instance- -- pass-through like MonadState- , MonadWriter w- , MonadError GhcModError- )--instance MonadIO m => MonadIO (GhcModT m) where- liftIO action = do- res <- GhcModT . liftIO . liftIO . liftIO . liftIO $ try action- case res of- Right a -> return a-- Left e | isIOError e ->- throwError $ GMEIOException (fromEx e :: IOError)- Left e | isGhcModError e ->- throwError $ (fromEx e :: GhcModError)- Left e -> throw e-- where- fromEx :: Exception e => SomeException -> e- fromEx se = let Just e = fromException se in e- isIOError se =- case fromException se of- Just (_ :: IOError) -> True- Nothing -> False-- isGhcModError se =- case fromException se of- Just (_ :: GhcModError) -> True- Nothing -> False---instance MonadTrans (GhcModT) where- lift = GhcModT . lift . lift . lift . lift--instance MonadState s m => MonadState s (GhcModT m) where- get = GhcModT $ lift $ lift $ lift get- put = GhcModT . lift . lift . lift . put- state = GhcModT . lift . lift . lift . state---#if MONADIO_INSTANCES-instance MonadIO m => MonadIO (StateT s m) where- liftIO = lift . liftIO--instance MonadIO m => MonadIO (ReaderT r m) where- liftIO = lift . liftIO+import Control.Applicative -instance (Monoid w, MonadIO m) => MonadIO (JournalT w m) where- liftIO = lift . liftIO+import Control.Concurrent -instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where- liftIO = lift . liftIO+import Control.Monad.Reader (runReaderT)+import Control.Monad.State.Strict (runStateT)+import Control.Monad.Trans.Journal (runJournalT) -instance MonadIO m => MonadIO (MaybeT m) where- liftIO = lift . liftIO-#endif+import Exception (ExceptionMonad(..)) -----------------------------------------------------------------+import System.Directory+import Prelude --- | Initialize the 'DynFlags' relating to the compilation of a single--- file or GHC session according to the 'Cradle' and 'Options'--- provided.-initializeFlagsWithCradle :: (IOish m, GhcMonad m, MonadError GhcModError m)- => Options- -> Cradle- -> m ()-initializeFlagsWithCradle opt c- | cabal = withCabal- | otherwise = withSandbox- where- mCabalFile = cradleCabalFile c- cabal = isJust mCabalFile- ghcopts = ghcUserOptions opt- withCabal = do- let Just cabalFile = mCabalFile- pkgDesc <- parseCabalFile c cabalFile- compOpts <- getCompilerOptions ghcopts c pkgDesc- initSession CabalPkg opt compOpts- withSandbox = initSession SingleFile opt compOpts- where- importDirs = [".","..","../..","../../..","../../../..","../../../../.."]- pkgOpts = ghcDbStackOpts $ cradlePkgDbStack c- compOpts- | null pkgOpts = CompilerOptions ghcopts importDirs []- | otherwise = CompilerOptions (ghcopts ++ pkgOpts) [wdir,rdir] []- wdir = cradleCurrentDir c- rdir = cradleRootDir c+withCradle :: IOish m => FilePath -> (Cradle -> m a) -> m a+withCradle cradledir f =+ gbracket (liftIO $ findCradle' cradledir) (liftIO . cleanupCradle) f -initSession :: GhcMonad m- => Build- -> Options- -> CompilerOptions- -> m ()-initSession build Options {..} CompilerOptions {..} = do- df <- G.getSessionDynFlags- void $ G.setSessionDynFlags =<< addCmdOpts ghcOptions- ( setModeSimple- $ Gap.setFlags- $ setIncludeDirs includeDirs- $ setBuildEnv build- $ setEmptyLogger- $ Gap.addPackageFlags depPackages df)+withGhcModEnv :: IOish m => FilePath -> Options -> (GhcModEnv -> m a) -> m a+withGhcModEnv dir opt f = withCradle dir (withGhcModEnv' opt f) -----------------------------------------------------------------+withGhcModEnv' :: IOish m => Options -> (GhcModEnv -> m a) -> Cradle -> m a+withGhcModEnv' opt f crdl = do+ olddir <- liftIO getCurrentDirectory+ c <- liftIO newChan+ let outp = case linePrefix opt of+ Just _ -> GmOutputChan c+ Nothing -> GmOutputStdio+ gbracket_ (setup c) (teardown olddir) (f $ GhcModEnv opt crdl outp)+ where+ setup c = liftIO $ do+ setCurrentDirectory $ cradleRootDir crdl+ forkIO $ stdoutGateway c -newGhcModEnv :: Options -> FilePath -> IO GhcModEnv-newGhcModEnv opt dir = do- session <- newIORef (error "empty session")- c <- findCradle' dir- return GhcModEnv {- gmGhcSession = session- , gmOptions = opt- , gmCradle = c- }+ teardown olddir tid = liftIO $ do+ setCurrentDirectory olddir+ killThread tid -cleanupGhcModEnv :: GhcModEnv -> IO ()-cleanupGhcModEnv env = cleanupCradle $ gmCradle env+ gbracket_ ma mb mc = gbracket ma mb (const mc) -- | Run a @GhcModT m@ computation. runGhcModT :: IOish m => Options -> GhcModT m a -> m (Either GhcModError a, GhcModLog)-runGhcModT opt action = gbracket newEnv delEnv $ \env -> do- r <- first (fst <$>) <$> (runGhcModT' env defaultState $ do- dflags <- getSessionDynFlags- defaultCleanupHandler dflags $ do- initializeFlagsWithCradle opt (gmCradle env)- action)- return r+runGhcModT opt action = do+ dir <- liftIO getCurrentDirectory+ runGhcModT' dir opt action - where- newEnv = liftBase $ newGhcModEnv opt =<< getCurrentDirectory- delEnv = liftBase . cleanupGhcModEnv+runGhcModT' :: IOish m+ => FilePath+ -> Options+ -> GhcModT m a+ -> m (Either GhcModError a, GhcModLog)+runGhcModT' dir opt action = liftIO (canonicalizePath dir) >>= \dir' ->+ withGhcModEnv dir' opt $ \env ->+ first (fst <$>) <$> runGhcModT'' env defaultGhcModState+ (gmSetLogLevel (logLevel opt) >> action) -- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT -- computation. Note that if the computation that returned @result@ modified the@@ -292,7 +103,7 @@ => (Either GhcModError a, GhcModLog) -> GhcModT m a hoistGhcModT (r,l) = do- GhcModT (lift $ lift $ journal l) >> case r of+ gmlJournal l >> case r of Left e -> throwError e Right a -> return a @@ -301,179 +112,10 @@ -- do with 'GhcModEnv' and 'GhcModState'. -- -- You should probably look at 'runGhcModT' instead.-runGhcModT' :: IOish m- => GhcModEnv- -> GhcModState- -> GhcModT m a- -> m (Either GhcModError (a, GhcModState), GhcModLog)-runGhcModT' r s a = do- (res, w') <-- flip runReaderT r $ runJournalT $ runErrorT $- runStateT (unGhcModT $ initGhcMonad (Just libdir) >> a) s- return (res, w')-------------------------------------------------------------------- | Make a copy of the 'gmGhcSession' IORef, run the action and restore the--- original 'HscEnv'.-withTempSession :: IOish m => GhcModT m a -> GhcModT m a-withTempSession action = do- session <- gmGhcSession <$> ask- savedHscEnv <- liftIO $ readIORef session- a <- action- liftIO $ writeIORef session savedHscEnv- return a---- | This is a very ugly workaround don't use it.-overrideGhcUserOptions :: IOish m => ([GHCOption] -> GhcModT m b) -> GhcModT m b-overrideGhcUserOptions action = withTempSession $ do- env <- ask- opt <- options- let ghcOpts = ghcUserOptions opt- opt' = opt { ghcUserOptions = [] }-- initializeFlagsWithCradle opt' (gmCradle env)-- action ghcOpts--------------------------------------------------------------------gmeAsk :: IOish m => GhcModT m GhcModEnv-gmeAsk = ask--gmsGet :: IOish m => GhcModT m GhcModState-gmsGet = GhcModT get--gmsPut :: IOish m => GhcModState -> GhcModT m ()-gmsPut = GhcModT . put--options :: IOish m => GhcModT m Options-options = gmOptions <$> gmeAsk--cradle :: IOish m => GhcModT m Cradle-cradle = gmCradle <$> gmeAsk--getCompilerMode :: IOish m => GhcModT m CompilerMode-getCompilerMode = gmCompilerMode <$> gmsGet--setCompilerMode :: IOish m => CompilerMode -> GhcModT m ()-setCompilerMode mode = (\s -> gmsPut s { gmCompilerMode = mode } ) =<< gmsGet--------------------------------------------------------------------withOptions :: IOish m => (Options -> Options) -> GhcModT m a -> GhcModT m a-withOptions changeOpt action = local changeEnv action- where- changeEnv e = e { gmOptions = changeOpt opt }- where- opt = gmOptions e--------------------------------------------------------------------instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where- liftBase = GhcModT . liftBase--#if MIN_VERSION_monad_control(1,0,0)--instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where- type StM (GhcModT m) a =- StM (StateT GhcModState- (ErrorT GhcModError- (JournalT GhcModLog- (ReaderT GhcModEnv m) ) ) ) a- liftBaseWith f = GhcModT . liftBaseWith $ \runInBase ->- f $ runInBase . unGhcModT-- restoreM = GhcModT . restoreM- {-# INLINE liftBaseWith #-}- {-# INLINE restoreM #-}--#else--instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where- newtype StM (GhcModT m) a = StGhcMod {- unStGhcMod :: StM (StateT GhcModState- (ErrorT GhcModError- (JournalT GhcModLog- (ReaderT GhcModEnv m) ) ) ) a }- liftBaseWith f = GhcModT . liftBaseWith $ \runInBase ->- f $ liftM StGhcMod . runInBase . unGhcModT-- restoreM = GhcModT . restoreM . unStGhcMod- {-# INLINE liftBaseWith #-}- {-# INLINE restoreM #-}--#endif---- GHC cannot prove the following instances to be decidable automatically using--- the FlexibleContexts extension as they violate the second Paterson Condition,--- namely that: The assertion has fewer constructors and variables (taken--- together and counting repetitions) than the head. Specifically the--- @MonadBaseControl IO m@ constraint is causing this violation.------ Proof of termination:------ Assuming all constraints containing the variable `m' exist and are decidable--- we show termination by manually replacing the current set of constraints with--- their own set of constraints and show that this, after a finite number of--- steps, results in the empty set, i.e. not having to check any more--- constraints.------ We start by setting the constraints to be those immediate constraints of the--- instance declaration which cannot be proven decidable automatically for the--- type under consideration.------ @--- { MonadBaseControl IO m }--- @------ Classes used:------ * @class MonadBase b m => MonadBaseControl b m@------ @--- { MonadBase IO m }--- @------ Classes used:------ * @class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase b m@------ @--- { Applicative IO, Applicative m, Monad IO, Monad m }--- @------ Classes used:------ * @class Monad m@--- * @class Applicative f => Functor f@------ @--- { Functor m }--- @------ Classes used:------ * @class Functor f@------ @--- { }--- @--- ∎--instance (Functor m, MonadIO m, MonadBaseControl IO m)- => GhcMonad (GhcModT m) where- getSession = (liftIO . readIORef) . gmGhcSession =<< ask- setSession a = (liftIO . flip writeIORef a) . gmGhcSession =<< ask--#if __GLASGOW_HASKELL__ >= 706-instance (Functor m, MonadIO m, MonadBaseControl IO m)- => HasDynFlags (GhcModT m) where- getDynFlags = getSessionDynFlags-#endif--instance (MonadIO m, MonadBaseControl IO m)- => ExceptionMonad (GhcModT m) where- gcatch act handler = control $ \run ->- run act `gcatch` (run . handler)-- gmask = liftBaseOp gmask . liftRestore- where liftRestore f r = f $ liftBaseOp_ r+runGhcModT'' :: IOish m+ => GhcModEnv+ -> GhcModState+ -> GhcModT m a+ -> m (Either GhcModError (a, GhcModState), GhcModLog)+runGhcModT'' r s a = do+ flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGhcModT a) s
+ Language/Haskell/GhcMod/Monad/Types.hs view
@@ -0,0 +1,442 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-}+{-# LANGUAGE TypeFamilies, UndecidableInstances, BangPatterns #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Language.Haskell.GhcMod.Monad.Types (+ -- * Monad Types+ GhcModT(..)+ , GmlT(..)+ , LightGhc(..)+ , GmGhc+ , IOish+ -- * Environment, state and logging+ , GhcModEnv(..)+ , GhcModState(..)+ , GhcModCaches(..)+ , defaultGhcModState+ , GmGhcSession(..)+ , GmComponent(..)+ , CompilerMode(..)+ -- * Accessing 'GhcModEnv', 'GhcModState' and 'GhcModLog'+ , GmLogLevel(..)+ , GhcModLog(..)+ , GhcModError(..)+ , Gm+ , GmEnv(..)+ , GmState(..)+ , GmLog(..)+ , cradle+ , options+ , withOptions+ , getCompilerMode+ , setCompilerMode+ -- * Re-exporting convenient stuff+ , MonadIO+ , liftIO+ , gmlGetSession+ , gmlSetSession+ ) where++-- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.+-- RWST does not automatically become an instance of MonadIO.+-- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.+-- So, RWST automatically becomes an instance of+#if __GLASGOW_HASKELL__ < 708+-- 'CoreMonad.MonadIO' and 'Control.Monad.IO.Class.MonadIO' are different+-- classes before ghc 7.8+#define DIFFERENT_MONADIO 1++-- RWST doen't have a MonadIO instance before ghc 7.8+#define MONADIO_INSTANCES 1+#endif++import Language.Haskell.GhcMod.Types++import GHC+import DynFlags+import Exception+import HscTypes++import Control.Applicative+import Control.Monad++import Control.Monad.Reader (ReaderT(..))+import Control.Monad.Error (ErrorT(..), MonadError(..))+import Control.Monad.State.Strict (StateT(..))+import Control.Monad.Trans.Journal (JournalT)+import Control.Monad.Trans.Maybe (MaybeT(..))++import Control.Monad.Base (MonadBase(..), liftBase)+import Control.Monad.Trans.Control++import Control.Monad.Reader.Class+import Control.Monad.Writer.Class+import Control.Monad.State.Class (MonadState(..))+import Control.Monad.Journal.Class (MonadJournal(..))+import Control.Monad.Trans.Class (MonadTrans(..))+import Control.Monad.Error (Error(..))+import qualified Control.Monad.IO.Class as MTL++#if DIFFERENT_MONADIO+import Data.Monoid (Monoid)+#endif++import Data.Maybe+import Data.Monoid+import Data.IORef+import Prelude++import qualified MonadUtils as GHC (MonadIO(..))++-- | This is basically a newtype wrapper around 'StateT', 'ErrorT', 'JournalT'+-- and 'ReaderT' with custom instances for 'GhcMonad' and it's constraints that+-- means you can run (almost) all functions from the GHC API on top of 'GhcModT'+-- transparently.+--+-- The inner monad @m@ should have instances for 'MonadIO' and+-- 'MonadBaseControl' 'IO', in the common case this is simply 'IO'. Most @mtl@+-- monads already have 'MonadBaseControl' 'IO' instances, see the+-- @monad-control@ package.+newtype GhcModT m a = GhcModT {+ unGhcModT :: StateT GhcModState+ (ErrorT GhcModError+ (JournalT GhcModLog+ (ReaderT GhcModEnv m) ) ) a+ } deriving ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , MTL.MonadIO+#if DIFFERENT_MONADIO+ , GHC.MonadIO+#endif+ , MonadError GhcModError+ )++newtype GmlT m a = GmlT { unGmlT :: GhcModT m a }+ deriving ( Functor+ , Applicative+ , Alternative+ , Monad+ , MonadPlus+ , MonadTrans+ , MTL.MonadIO+#if DIFFERENT_MONADIO+ , GHC.MonadIO+#endif+ , MonadError GhcModError+ , GmEnv+ , GmState+ , GmLog+ )++newtype LightGhc a = LightGhc { unLightGhc :: ReaderT (IORef HscEnv) IO a }+ deriving ( Functor+ , Applicative+ , Monad+ , MTL.MonadIO+#if DIFFERENT_MONADIO+ , GHC.MonadIO+#endif+ )++#if DIFFERENT_MONADIO+instance MTL.MonadIO m => GHC.MonadIO (ReaderT x m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (StateT x m) where+ liftIO = MTL.liftIO+instance (Error e, MTL.MonadIO m) => GHC.MonadIO (ErrorT e m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (JournalT x m) where+ liftIO = MTL.liftIO+instance MTL.MonadIO m => GHC.MonadIO (MaybeT m) where+ liftIO = MTL.liftIO+#endif++instance MonadIO IO where+ liftIO = id+instance MonadIO m => MonadIO (ReaderT x m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (StateT x m) where+ liftIO = MTL.liftIO+instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (JournalT x m) where+ liftIO = MTL.liftIO+instance MonadIO m => MonadIO (MaybeT m) where+ liftIO = MTL.liftIO+instance MonadIOC m => MonadIO (GhcModT m) where+ liftIO = MTL.liftIO+instance MonadIOC m => MonadIO (GmlT m) where+ liftIO = MTL.liftIO+instance MonadIO LightGhc where+ liftIO = MTL.liftIO++class Monad m => GmEnv m where+ gmeAsk :: m GhcModEnv+ gmeAsk = gmeReader id++ gmeReader :: (GhcModEnv -> a) -> m a+ gmeReader f = f `liftM` gmeAsk++ gmeLocal :: (GhcModEnv -> GhcModEnv) -> m a -> m a+ {-# MINIMAL (gmeAsk | gmeReader), gmeLocal #-}++type Gm m = (GmEnv m, GmState m, GmLog m)++instance Monad m => GmEnv (GhcModT m) where+ gmeAsk = GhcModT ask+ gmeReader = GhcModT . reader+ gmeLocal f a = GhcModT $ local f (unGhcModT a)++instance GmEnv m => GmEnv (StateT s m) where+ gmeAsk = lift gmeAsk+ gmeReader = lift . gmeReader+ gmeLocal f (StateT a) = StateT $ \s -> gmeLocal f (a s)++class Monad m => GmState m where+ gmsGet :: m GhcModState+ gmsGet = gmsState (\s -> (s, s))++ gmsPut :: GhcModState -> m ()+ gmsPut s = gmsState (\_ -> ((), s))++ gmsState :: (GhcModState -> (a, GhcModState)) -> m a+ gmsState f = do+ s <- gmsGet+ let ~(a, s') = f s+ gmsPut s'+ return a+ {-# MINIMAL gmsState | gmsGet, gmsPut #-}++instance Monad m => GmState (StateT GhcModState m) where+ gmsGet = get+ gmsPut = put+ gmsState = state++instance Monad m => GmState (GhcModT m) where+ gmsGet = GhcModT get+ gmsPut = GhcModT . put+ gmsState = GhcModT . state++instance GmState m => GmState (MaybeT m) where+ gmsGet = MaybeT $ Just `liftM` gmsGet+ gmsPut = MaybeT . (Just `liftM`) . gmsPut+ gmsState = MaybeT . (Just `liftM`) . gmsState++class Monad m => GmLog m where+ gmlJournal :: GhcModLog -> m ()+ gmlHistory :: m GhcModLog+ gmlClear :: m ()++instance Monad m => GmLog (JournalT GhcModLog m) where+ gmlJournal = journal+ gmlHistory = history+ gmlClear = clear++instance Monad m => GmLog (GhcModT m) where+ gmlJournal = GhcModT . lift . lift . journal+ gmlHistory = GhcModT $ lift $ lift history+ gmlClear = GhcModT $ lift $ lift clear++instance (Monad m, GmLog m) => GmLog (ReaderT r m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++instance (Monad m, GmLog m) => GmLog (StateT s m) where+ gmlJournal = lift . gmlJournal+ gmlHistory = lift gmlHistory+ gmlClear = lift gmlClear++instance Monad m => MonadJournal GhcModLog (GhcModT m) where+ journal !w = GhcModT $ lift $ lift $ (journal w)+ history = GhcModT $ lift $ lift $ history+ clear = GhcModT $ lift $ lift $ clear++instance MonadTrans GhcModT where+ lift = GhcModT . lift . lift . lift . lift++instance forall r m. MonadReader r m => MonadReader r (GhcModT m) where+ local f ma = gmLiftWithInner (\run -> local f (run ma))+ ask = gmLiftInner ask++instance (Monoid w, MonadWriter w m) => MonadWriter w (GhcModT m) where+ tell = gmLiftInner . tell+ listen ma =+ liftWith (\run -> listen (run ma)) >>= \(sta, w) ->+ flip (,) w `liftM` restoreT (return sta)++ pass maww = maww >>= gmLiftInner . pass . return++instance MonadState s m => MonadState s (GhcModT m) where+ get = GhcModT $ lift $ lift $ lift get+ put = GhcModT . lift . lift . lift . put+ state = GhcModT . lift . lift . lift . state++instance (MonadBaseControl IO m) => MonadBase IO (GmlT m) where+ liftBase = GmlT . liftBase++instance (MonadBaseControl IO m) => MonadBaseControl IO (GmlT m) where+ type StM (GmlT m) a = StM (GhcModT m) a+ liftBaseWith = defaultLiftBaseWith+ restoreM = defaultRestoreM+ {-# INLINE liftBaseWith #-}+ {-# INLINE restoreM #-}++instance MonadTransControl GmlT where+ type StT GmlT a = StT GhcModT a+ liftWith = defaultLiftWith GmlT unGmlT+ restoreT = defaultRestoreT GmlT++instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where+ liftBase = GhcModT . liftBase++instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where+ type StM (GhcModT m) a =+ StM (StateT GhcModState+ (ErrorT GhcModError+ (JournalT GhcModLog+ (ReaderT GhcModEnv m) ) ) ) a++ liftBaseWith f = GhcModT (liftBaseWith $ \runInBase ->+ f $ runInBase . unGhcModT)++ restoreM = GhcModT . restoreM+ {-# INLINE liftBaseWith #-}+ {-# INLINE restoreM #-}++instance MonadTransControl GhcModT where+ type StT GhcModT a = (Either GhcModError (a, GhcModState), GhcModLog)++ liftWith f = GhcModT $+ liftWith $ \runS ->+ liftWith $ \runE ->+ liftWith $ \runJ ->+ liftWith $ \runR ->+ f $ \ma -> runR $ runJ $ runE $ runS $ unGhcModT ma+ restoreT = GhcModT . restoreT . restoreT . restoreT . restoreT+ {-# INLINE liftWith #-}+ {-# INLINE restoreT #-}++gmLiftInner :: Monad m => m a -> GhcModT m a+gmLiftInner = GhcModT . lift . lift . lift . lift++gmLiftWithInner :: (MonadTransControl t, Monad m, Monad (t m))+ => (Run t -> m (StT t a)) -> t m a+gmLiftWithInner f = liftWith f >>= restoreT . return++-- GHC cannot prove the following instances to be decidable automatically using+-- the FlexibleContexts extension as they violate the second Paterson Condition,+-- namely that: The assertion has fewer constructors and variables (taken+-- together and counting repetitions) than the head. Specifically the+-- @MonadBaseControl IO m@ constraint in 'IOish' is causing this violation.++type GmGhc m = (IOish m, GhcMonad m)++instance (MonadIO m, MonadBaseControl IO m) => GhcMonad (GmlT m) where+ getSession = gmlGetSession+ setSession = gmlSetSession++-- ---------------------------------------------------------------------++gmlGetSession :: (MonadIO m, MonadBaseControl IO m) => GmlT m HscEnv+gmlGetSession = do+ ref <- gmgsSession . fromJust . gmGhcSession <$> gmsGet+ GHC.liftIO $ readIORef ref++gmlSetSession :: (MonadIO m, MonadBaseControl IO m) => HscEnv -> GmlT m ()+gmlSetSession a = do+ ref <- gmgsSession . fromJust . gmGhcSession <$> gmsGet+ GHC.liftIO $ flip writeIORef a ref++-- ---------------------------------------------------------------------+instance GhcMonad LightGhc where+ getSession = (GHC.liftIO . readIORef) =<< LightGhc ask+ setSession a = (GHC.liftIO . flip writeIORef a) =<< LightGhc ask++#if __GLASGOW_HASKELL__ >= 706+instance (MonadIO m, MonadBaseControl IO m) => HasDynFlags (GmlT m) where+ getDynFlags = hsc_dflags <$> getSession++instance HasDynFlags LightGhc where+ getDynFlags = hsc_dflags <$> getSession+#endif++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GhcModT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmlT m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance ExceptionMonad LightGhc where+ gcatch act handl =+ LightGhc $ unLightGhc act `gcatch` \e -> unLightGhc (handl e)+ gmask f =+ LightGhc $ gmask $ \io_restore ->let+ g_restore (LightGhc m) = LightGhc $ io_restore m+ in+ unLightGhc (f g_restore)+++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (StateT s m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (ReaderT s m) where+ gcatch act handler = control $ \run ->+ run act `gcatch` (run . handler)++ gmask = liftBaseOp gmask . liftRestore+ where liftRestore f r = f $ liftBaseOp_ r++----------------------------------------------------------------++options :: GmEnv m => m Options+options = gmOptions `liftM` gmeAsk++cradle :: GmEnv m => m Cradle+cradle = gmCradle `liftM` gmeAsk++getCompilerMode :: GmState m => m CompilerMode+getCompilerMode = gmCompilerMode `liftM` gmsGet++setCompilerMode :: GmState m => CompilerMode -> m ()+setCompilerMode mode = (\s -> gmsPut s { gmCompilerMode = mode } ) =<< gmsGet++withOptions :: GmEnv m => (Options -> Options) -> m a -> m a+withOptions changeOpt action = gmeLocal changeEnv action+ where+ changeEnv e = e { gmOptions = changeOpt opt }+ where+ opt = gmOptions e
+ Language/Haskell/GhcMod/Output.hs view
@@ -0,0 +1,199 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++-- Derived from process:System.Process+-- Copyright (c) The University of Glasgow 2004-2008++module Language.Haskell.GhcMod.Output (+ gmPutStr+ , gmErrStr+ , gmPutStrLn+ , gmErrStrLn+ , gmUnsafePutStrLn+ , gmUnsafeErrStrLn+ , gmReadProcess+ , stdoutGateway+ ) where++import Data.List+import System.IO+import System.Exit+import System.Process+import Control.Monad+import Control.DeepSeq+import Control.Exception+import Control.Concurrent++import Language.Haskell.GhcMod.Types hiding (LineSeparator)+import Language.Haskell.GhcMod.Monad.Types++withLines :: (String -> String) -> String -> String+withLines f s = let+ res = unlines $ map f $ lines s+ in+ case s of+ [] -> res+ _ | not $ isTerminated s ->+ reverse $ drop 1 $ reverse res+ _ -> res++isTerminated :: String -> Bool+isTerminated "" = False+isTerminated s = isNewline (last s)++isNewline :: Char -> Bool+isNewline c = c == '\n'++toGmLines :: String -> (GmLines String)+toGmLines "" = GmLines GmPartial ""+toGmLines s | isNewline (last s) = GmLines GmTerminated s+toGmLines s = GmLines GmPartial s++outputFns :: (GmEnv m, MonadIO m')+ => m (GmLines String -> m' (), GmLines String -> m' ())+outputFns = do+ opts <- options+ env <- gmeAsk+ return $ outputFns' opts (gmOutput env)++outputFns' :: MonadIO m'+ => Options+ -> GmOutput+ -> (GmLines String -> m' (), GmLines String -> m' ())+outputFns' opts output = let+ Options {..} = opts++ pfx f = withLines f++ outPfx, errPfx :: GmLines String -> GmLines String+ (outPfx, errPfx) =+ case linePrefix of+ Nothing -> ( id, id )+ Just (op, ep) -> ( fmap $ pfx (op++), fmap $ pfx (ep++) )+ in+ case output of+ GmOutputStdio ->+ ( liftIO . putStr . unGmLine . outPfx+ , liftIO . hPutStr stderr . unGmLine . errPfx)+ GmOutputChan c ->+ ( liftIO . writeChan c . (,) GmOut . outPfx+ , liftIO . writeChan c . (,) GmErr .errPfx)++gmPutStr, gmPutStrLn, gmErrStr, gmErrStrLn+ :: (MonadIO m, GmEnv m) => String -> m ()++gmPutStr str = do+ putOut <- fst `liftM` outputFns+ putOut $ toGmLines str++gmPutStrLn = gmPutStr . (++"\n")+gmErrStrLn = gmErrStr . (++"\n")++gmErrStr str = do+ putErr <- snd `liftM` outputFns+ putErr $ toGmLines str++-- | Only use these when you're sure there are no other writers on stdout+gmUnsafePutStrLn, gmUnsafeErrStrLn+ :: MonadIO m => Options -> String -> m ()+gmUnsafePutStrLn opts = (fst $ outputFns' opts GmOutputStdio) . toGmLines+gmUnsafeErrStrLn opts = (snd $ outputFns' opts GmOutputStdio) . toGmLines++gmReadProcess :: GmEnv m => m (FilePath -> [String] -> String -> IO String)+gmReadProcess = do+ GhcModEnv {..} <- gmeAsk+ case gmOutput of+ GmOutputChan _ ->+ readProcessStderrChan+ GmOutputStdio ->+ return $ readProcess++stdoutGateway :: Chan (GmStream, GmLines String) -> IO ()+stdoutGateway chan = go ("", "")+ where+ go buf@(obuf, ebuf) = do+ (stream, GmLines ty l) <- readChan chan+ case ty of+ GmTerminated ->+ case stream of+ GmOut -> putStr (obuf++l) >> go ("", ebuf)+ GmErr -> putStr (ebuf++l) >> go (obuf, "")+ GmPartial -> case reverse $ lines l of+ [] -> go buf+ [x] -> go (appendBuf stream buf x)+ x:xs -> do+ putStr $ unlines $ reverse xs+ go (appendBuf stream buf x)++ appendBuf GmOut (obuf, ebuf) s = (obuf++s, ebuf)+ appendBuf GmErr (obuf, ebuf) s = (obuf, ebuf++s)+++readProcessStderrChan ::+ GmEnv m => m (FilePath -> [String] -> String -> IO String)+readProcessStderrChan = do+ (_, e) <- outputFns+ return $ go e+ where+ go :: (GmLines String -> IO ()) -> FilePath -> [String] -> String -> IO String+ go putErr exe args input = do+ let cp = (proc exe args) {+ std_out = CreatePipe+ , std_err = CreatePipe+ , std_in = CreatePipe+ }+ (Just i, Just o, Just e, h) <- createProcess cp++ _ <- forkIO $ reader e++ output <- hGetContents o+ withForkWait (evaluate $ rnf output) $ \waitOut -> do++ -- now write any input+ unless (null input) $+ ignoreSEx $ hPutStr i input+ -- hClose performs implicit hFlush, and thus may trigger a SIGPIPE+ ignoreSEx $ hClose i++ -- wait on the output+ waitOut+ hClose o++ res <- waitForProcess h+ case res of+ ExitFailure rv ->+ processFailedException "readProcessStderrChan" exe args rv+ ExitSuccess ->+ return output+ where+ ignoreSEx = handle (\(SomeException _) -> return ())+ reader h = ignoreSEx $ do+ putErr . toGmLines . (++"\n") =<< hGetLine h+ reader h++withForkWait :: IO () -> (IO () -> IO a) -> IO a+withForkWait async body = do+ waitVar <- newEmptyMVar :: IO (MVar (Either SomeException ()))+ mask $ \restore -> do+ tid <- forkIO $ try (restore async) >>= putMVar waitVar+ let wait = takeMVar waitVar >>= either throwIO return+ restore (body wait) `onException` killThread tid++processFailedException :: String -> String -> [String] -> Int -> IO a+processFailedException fn exe args rv =+ error $ concat [ fn, ": ", exe, " "+ , intercalate " " (map show args)+ , " (exit " ++ show rv ++ ")"]
Language/Haskell/GhcMod/PathsAndFiles.hs view
@@ -1,21 +1,42 @@-{-# LANGUAGE BangPatterns, TupleSections #-}-module Language.Haskell.GhcMod.PathsAndFiles where+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>. +module Language.Haskell.GhcMod.PathsAndFiles (+ module Language.Haskell.GhcMod.PathsAndFiles+ , module Language.Haskell.GhcMod.Caching+ ) where++import Config (cProjectVersion) import Control.Applicative import Control.Monad import Data.List import Data.Char import Data.Maybe-import Data.Traversable (traverse)-import Language.Haskell.GhcMod.Types+import Data.Traversable hiding (mapM)+import Distribution.Helper (buildPlatform) import System.Directory import System.FilePath+import System.Process +import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Error+import Language.Haskell.GhcMod.Caching import qualified Language.Haskell.GhcMod.Utils as U--import Distribution.Simple.BuildPaths (defaultDistPref)-import Distribution.Simple.Configure (localBuildInfoFile)+import Utils (mightExist)+import Prelude -- | Guaranteed to be a path to a directory with no trailing slash. type DirPath = FilePath@@ -23,40 +44,111 @@ -- | Guaranteed to be the name of a file only (no slashes). type FileName = String +newtype UnString = UnString { unString :: String }++instance Show UnString where+ show = unString++instance Read UnString where+ readsPrec _ = \str -> [(UnString str, "")]+ -- | @findCabalFiles dir@. Searches for a @.cabal@ files in @dir@'s parent -- directories. The first parent directory containing more than one cabal file -- is assumed to be the project directory. If only one cabal file exists in this -- directory it is returned otherwise @findCabalFiles@ throws 'GMENoCabalFile' -- or 'GMETooManyCabalFiles' findCabalFile :: FilePath -> IO (Maybe FilePath)-findCabalFile directory = do- -- Look for cabal files in @dir@ and all it's parent directories- dcs <- getCabalFiles `zipMapM` parents directory- -- Extract first non-empty list, which represents a directory with cabal- -- files.- case find (not . null) $ uncurry appendDir `map` dcs of- Just [] -> throw $ GMENoCabalFile+findCabalFile dir = do+ -- List of directories and all cabal file candidates+ dcs <- findFileInParentsP isCabalFile dir :: IO ([(DirPath, [FileName])])+ let css = uncurry appendDir `map` dcs :: [[FilePath]]+ case find (not . null) css of+ Nothing -> return Nothing Just cfs@(_:_:_) -> throw $ GMETooManyCabalFiles cfs- a -> return $ head <$> a+ Just (a:_) -> return (Just a)+ Just [] -> error "findCabalFile" where appendDir :: DirPath -> [FileName] -> [FilePath]- appendDir dir fs = (dir </>) `map` fs+ appendDir d fs = (d </>) `map` fs --- | @getCabalFiles dir@. Find all files ending in @.cabal@ in @dir@.-getCabalFiles :: DirPath -> IO [FileName]-getCabalFiles dir =- filterM isCabalFile =<< getDirectoryContents dir+-- | Get path to sandbox config file+getSandboxDb :: FilePath+ -- ^ Path to the cabal package root directory (containing the+ -- @cabal.sandbox.config@ file)+ -> IO (Maybe GhcPkgDb)+getSandboxDb d = do+ mConf <- traverse readFile =<< mightExist (d </> "cabal.sandbox.config")+ bp <- buildPlatform readProcess+ return $ PackageDb . fixPkgDbVer bp <$> (extractSandboxDbDir =<< mConf)+ where- isCabalFile f = do- exists <- doesFileExist $ dir </> f- return (exists && takeExtension' f == ".cabal")+ fixPkgDbVer bp dir =+ case takeFileName dir == ghcSandboxPkgDbDir bp of+ True -> dir+ False -> takeDirectory dir </> ghcSandboxPkgDbDir bp - takeExtension' p = if takeFileName p == takeExtension p- then ""- else takeExtension p+-- | Extract the sandbox package db directory from the cabal.sandbox.config+-- file. Exception is thrown if the sandbox config file is broken.+extractSandboxDbDir :: String -> Maybe FilePath+extractSandboxDbDir conf = extractValue <$> parse conf+ where+ key = "package-db:"+ keyLen = length key + parse = listToMaybe . filter (key `isPrefixOf`) . lines+ extractValue = U.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen+++-- |+-- >>> isCabalFile "/home/user/.cabal"+-- False+isCabalFile :: FilePath -> Bool+isCabalFile f = takeExtension' f == ".cabal"++-- |+-- >>> takeExtension' "/some/dir/bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' "some/reldir/bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' "bla.cabal"+-- ".cabal"+--+-- >>> takeExtension' ".cabal"+-- ""+takeExtension' :: FilePath -> String+takeExtension' p =+ if takeFileName p == takeExtension p+ then "" -- just ".cabal" is not a valid cabal file+ else takeExtension p++-- | @findFileInParentsP p dir@ Look for files satisfying @p@ in @dir@ and all+-- it's parent directories.+findFileInParentsP :: (FilePath -> Bool) -> FilePath+ -> IO [(DirPath, [FileName])]+findFileInParentsP p dir =+ getFilesP p `zipMapM` parents dir++-- | @getFilesP p dir@. Find all __files__ satisfying @p@ in @.cabal@ in @dir@.+getFilesP :: (FilePath -> Bool) -> DirPath -> IO [FileName]+getFilesP p dir = filterM p' =<< getDirectoryContents dir+ where+ p' fn = do+ (p fn && ) <$> doesFileExist (dir </> fn)++findCabalSandboxDir :: FilePath -> IO (Maybe FilePath)+findCabalSandboxDir dir = do+ dss <- findFileInParentsP isSandboxConfig dir+ return $ case find (not . null . snd) $ dss of+ Just (sbDir, _:_) -> Just sbDir+ _ -> Nothing++ where+ isSandboxConfig = (==sandboxConfigFile)+ zipMapM :: Monad m => (a -> m c) -> [a] -> m [(a,c)]-zipMapM f as = mapM (\a -> liftM (a,) $ f a) as+zipMapM f as = mapM (\a -> liftM ((,) a) $ f a) as -- | @parents dir@. Returns all parent directories of @dir@ including @dir@. --@@ -86,31 +178,48 @@ ---------------------------------------------------------------- --- | Get path to sandbox config file-getSandboxDb :: FilePath -- ^ Path to the cabal package root directory- -- (containing the @cabal.sandbox.config@ file)- -> IO (Maybe FilePath)-getSandboxDb d = do- mConf <- traverse readFile =<< U.mightExist (d </> "cabal.sandbox.config")- return $ extractSandboxDbDir =<< mConf---- | Extract the sandbox package db directory from the cabal.sandbox.config file.--- Exception is thrown if the sandbox config file is broken.-extractSandboxDbDir :: String -> Maybe FilePath-extractSandboxDbDir conf = extractValue <$> parse conf- where- key = "package-db:"- keyLen = length key-- parse = listToMaybe . filter (key `isPrefixOf`) . lines- extractValue = U.dropWhileEnd isSpace . dropWhile isSpace . drop keyLen- setupConfigFile :: Cradle -> FilePath setupConfigFile crdl = cradleRootDir crdl </> setupConfigPath +sandboxConfigFile :: FilePath+sandboxConfigFile = "cabal.sandbox.config"+ -- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@ setupConfigPath :: FilePath-setupConfigPath = localBuildInfoFile defaultDistPref+setupConfigPath = "dist/setup-config" -- localBuildInfoFile defaultDistPref +macrosHeaderPath :: FilePath+macrosHeaderPath = "dist/build/autogen/cabal_macros.h"++ghcSandboxPkgDbDir :: String -> String+ghcSandboxPkgDbDir buildPlatf = do+ buildPlatf ++ "-ghc-" ++ cProjectVersion ++ "-packages.conf.d"+ packageCache :: String packageCache = "package.cache"++-- | Filename of the symbol table cache file.+symbolCache :: Cradle -> FilePath+symbolCache crdl = cradleTempDir crdl </> symbolCacheFile++symbolCacheFile :: String+symbolCacheFile = "ghc-mod.symbol-cache"++resolvedComponentsCacheFile :: String+resolvedComponentsCacheFile = setupConfigPath <.> "ghc-mod.resolved-components"++cabalHelperCacheFile :: String+cabalHelperCacheFile = setupConfigPath <.> "ghc-mod.cabal-components"++mergedPkgOptsCacheFile :: String+mergedPkgOptsCacheFile = setupConfigPath <.> "ghc-mod.package-options"++pkgDbStackCacheFile :: String+pkgDbStackCacheFile = setupConfigPath <.> "ghc-mod.package-db-stack"++-- | @findCustomPackageDbFile dir@. Searches for a @.ghc-mod.cradle@ file in @dir@.+-- If it exists in the given directory it is returned otherwise @findCradleFile@ returns @Nothing@+findCustomPackageDbFile :: FilePath -> IO (Maybe FilePath)+findCustomPackageDbFile directory = do+ let path = directory </> "ghc-mod.package-db-stack"+ mightExist path
Language/Haskell/GhcMod/PkgDoc.hs view
@@ -5,22 +5,23 @@ import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Utils -import Control.Applicative ((<$>))+import Control.Applicative+import Prelude -- | Obtaining the package name and the doc path of a module. pkgDoc :: IOish m => String -> GhcModT m String pkgDoc mdl = do- c <- cradle- pkg <- trim <$> readProcess' "ghc-pkg" (toModuleOpts c)+ pkgDbStack <- getPackageDbStack+ pkg <- liftIO $ trim <$> readProcess "ghc-pkg" (toModuleOpts pkgDbStack) "" if pkg == "" then return "\n" else do- htmlpath <- readProcess' "ghc-pkg" (toDocDirOpts pkg c)+ htmlpath <- liftIO $ readProcess "ghc-pkg" (toDocDirOpts pkg pkgDbStack) "" let ret = pkg ++ " " ++ drop 14 htmlpath return ret where- toModuleOpts c = ["find-module", mdl, "--simple-output"]- ++ ghcPkgDbStackOpts (cradlePkgDbStack c)- toDocDirOpts pkg c = ["field", pkg, "haddock-html"]- ++ ghcPkgDbStackOpts (cradlePkgDbStack c)+ toModuleOpts dbs = ["find-module", mdl, "--simple-output"]+ ++ ghcPkgDbStackOpts dbs+ toDocDirOpts pkg dbs = ["field", pkg, "haddock-html"]+ ++ ghcPkgDbStackOpts dbs trim = takeWhile (`notElem` " \n")
+ Language/Haskell/GhcMod/Pretty.hs view
@@ -0,0 +1,69 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++module Language.Haskell.GhcMod.Pretty where++import Control.Arrow hiding ((<+>))+import Data.Char+import Data.List+import Distribution.Helper+import Text.PrettyPrint++import Language.Haskell.GhcMod.Types++docStyle :: Style+docStyle = style { ribbonsPerLine = 1.2 }++gmRenderDoc :: Doc -> String+gmRenderDoc = renderStyle docStyle++gmComponentNameDoc :: ChComponentName -> Doc+gmComponentNameDoc ChSetupHsName = text $ "Setup.hs"+gmComponentNameDoc ChLibName = text $ "library"+gmComponentNameDoc (ChExeName n) = text $ "exe:" ++ n+gmComponentNameDoc (ChTestName n) = text $ "test:" ++ n+gmComponentNameDoc (ChBenchName n) = text $ "bench:" ++ n++gmLogLevelDoc :: GmLogLevel -> Doc+gmLogLevelDoc GmSilent = error "GmSilent MUST not be used for log messages"+gmLogLevelDoc GmPanic = text "PANIC"+gmLogLevelDoc GmException = text "EXCEPTION"+gmLogLevelDoc GmError = text "ERROR"+gmLogLevelDoc GmWarning = text "Warning"+gmLogLevelDoc GmInfo = text "info"+gmLogLevelDoc GmDebug = text "DEBUG"+gmLogLevelDoc GmVomit = text "VOMIT"++infixl 6 <+>:+(<+>:) :: Doc -> Doc -> Doc+a <+>: b = (a <> colon) <+> b++fnDoc :: FilePath -> Doc+fnDoc = doubleQuotes . text++showDoc :: Show a => a -> Doc+showDoc = text . show++warnDoc :: Doc -> Doc+warnDoc d = text "Warning" <+>: d++strDoc :: String -> Doc+strDoc str = doc (dropWhileEnd isSpace str)+ where+ doc :: String -> Doc+ doc = lines+ >>> map (words >>> map text >>> fsep)+ >>> \l -> case l of (x:xs) -> hang x 4 (vcat xs); [] -> empty
Language/Haskell/GhcMod/SrcUtils.hs view
@@ -3,7 +3,7 @@ module Language.Haskell.GhcMod.SrcUtils where -import Control.Applicative ((<$>))+import Control.Applicative import CoreUtils (exprType) import Data.Generics import Data.Maybe (fromMaybe)@@ -13,15 +13,13 @@ import GHC.SYB.Utils (Stage(..), everythingStaged) import GhcMonad import qualified Language.Haskell.Exts.Annotated as HE-import Language.Haskell.GhcMod.Doc (showOneLine, getStyle)-import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.Gap (HasType(..), setWarnTypedHoles, setDeferTypeErrors)+import Language.Haskell.GhcMod.Doc+import Language.Haskell.GhcMod.Gap import qualified Language.Haskell.GhcMod.Gap as Gap-import Language.Haskell.GhcMod.Monad (IOish, GhcModT)-import Language.Haskell.GhcMod.Target (setTargetFiles) import OccName (OccName) import Outputable (PprStyle) import TcHsSyn (hsPatType)+import Prelude ---------------------------------------------------------------- @@ -82,22 +80,6 @@ pretty :: DynFlags -> PprStyle -> Type -> String pretty dflag style = showOneLine dflag style . Gap.typeForUser--------------------------------------------------------------------inModuleContext :: IOish m- => FilePath- -> (DynFlags -> PprStyle -> GhcModT m a)- -> GhcModT m a-inModuleContext file action =- withDynFlags (setWarnTypedHoles . setDeferTypeErrors . setNoWarningFlags) $ do- setTargetFiles [file]- Gap.withContext $ do- dflag <- G.getSessionDynFlags- style <- getStyle- action dflag style------------------------------------------------------------------ showName :: DynFlags -> PprStyle -> G.Name -> String showName dflag style name = showOneLine dflag style $ Gap.nameForUser name
Language/Haskell/GhcMod/Target.hs view
@@ -1,60 +1,486 @@-{-# LANGUAGE CPP #-}-module Language.Haskell.GhcMod.Target (- setTargetFiles- ) where+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>. -import Control.Applicative ((<$>))-import Control.Monad (forM, void, (>=>))-import DynFlags (ExtensionFlag(..), xopt)-import GHC (LoadHowMuch(..))-import qualified GHC as G+{-# LANGUAGE CPP, ViewPatterns, NamedFieldPuns, RankNTypes #-}+module Language.Haskell.GhcMod.Target where++import Control.Arrow+import Control.Applicative+import Control.Category ((.))+import Control.Monad.Reader (runReaderT)+import GHC+import GHC.Paths (libdir)+import StaticFlags+import SysTools+import DynFlags+import HscMain+import HscTypes+ import Language.Haskell.GhcMod.DynFlags-import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.Monad.Types+import Language.Haskell.GhcMod.CabalHelper+import Language.Haskell.GhcMod.HomeModuleGraph+import Language.Haskell.GhcMod.PathsAndFiles+import Language.Haskell.GhcMod.GhcPkg+import Language.Haskell.GhcMod.Error+import Language.Haskell.GhcMod.Logging+import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Utils as U ++import Data.Maybe+import Data.Monoid as Monoid+import Data.Either+import Data.Foldable as Foldable (foldrM)+import qualified Data.Foldable as Foldable+import Data.Traversable hiding (mapM, forM)+import Data.IORef+import Data.List+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import Distribution.Helper+import Prelude hiding ((.))++import System.Directory+import System.FilePath++withLightHscEnv :: forall m a. IOish m+ => [GHCOption] -> (HscEnv -> m a) -> m a+withLightHscEnv opts action = gbracket initEnv teardownEnv action+ where+ teardownEnv :: HscEnv -> m ()+ teardownEnv env = liftIO $ do+ let dflags = hsc_dflags env+ cleanTempFiles dflags+ cleanTempDirs dflags++ initEnv :: m HscEnv+ initEnv = liftIO $ do+ initStaticOpts+ settings <- initSysTools (Just libdir)+ dflags <- initDynFlags (defaultDynFlags settings)+ env <- newHscEnv dflags+ dflags' <- runLightGhc env $ do+ -- HomeModuleGraph and probably all other clients get into all sorts of+ -- trouble if the package state isn't initialized here+ _ <- setSessionDynFlags =<< addCmdOpts opts =<< getSessionDynFlags+ getSessionDynFlags+ newHscEnv dflags'++runLightGhc :: HscEnv -> LightGhc a -> IO a+runLightGhc env action = do+ renv <- newIORef env+ flip runReaderT renv $ unLightGhc action++runGmPkgGhc :: (IOish m, GmEnv m, GmState m, GmLog m) => LightGhc a -> m a+runGmPkgGhc action = do+ pkgOpts <- packageGhcOptions+ withLightHscEnv pkgOpts $ \env -> liftIO $ runLightGhc env action++initSession :: IOish m+ => [GHCOption] -> (DynFlags -> Ghc DynFlags) -> GhcModT m ()+initSession opts mdf = do+ s <- gmsGet+ case gmGhcSession s of+ Just GmGhcSession {..} -> when (gmgsOptions /= opts) $ putNewSession s+ Nothing -> putNewSession s++ where+ putNewSession s = do+ rghc <- (liftIO . newIORef =<< newSession =<< cradle)+ gmsPut s { gmGhcSession = Just $ GmGhcSession opts rghc }++ newSession Cradle { cradleTempDir } = liftIO $ do+ runGhc (Just libdir) $ do+ let setDf df = setTmpDir cradleTempDir <$> (mdf =<< addCmdOpts opts df)+ _ <- setSessionDynFlags =<< setDf =<< getSessionDynFlags+ getSession++-- | Drop the currently active GHC session, the next that requires a GHC session+-- will initialize a new one.+dropSession :: IOish m => GhcModT m ()+dropSession = do+ s <- gmsGet+ case gmGhcSession s of+ Just (GmGhcSession _opts ref) -> do+ -- TODO: This is still not enough, there seem to still be references to+ -- GHC's state around afterwards.+ liftIO $ writeIORef ref (error "HscEnv: session was dropped")+ -- Not available on ghc<7.8; didn't really help anyways+ -- liftIO $ setUnsafeGlobalDynFlags (error "DynFlags: session was dropped")+ gmsPut s { gmGhcSession = Nothing }++ Nothing -> return ()+++runGmlT :: IOish m => [Either FilePath ModuleName] -> GmlT m a -> GhcModT m a+runGmlT fns action = runGmlT' fns return action++runGmlT' :: IOish m+ => [Either FilePath ModuleName]+ -> (DynFlags -> Ghc DynFlags)+ -> GmlT m a+ -> GhcModT m a+runGmlT' fns mdf action = runGmlTWith fns mdf id action++runGmlTWith :: IOish m+ => [Either FilePath ModuleName]+ -> (DynFlags -> Ghc DynFlags)+ -> (GmlT m a -> GmlT m b)+ -> GmlT m a+ -> GhcModT m b+runGmlTWith efnmns' mdf wrapper action = do+ crdl <- cradle+ Options { ghcUserOptions } <- options++ let (fns, mns) = partitionEithers efnmns'+ ccfns = map (cradleCurrentDir crdl </>) fns+ cfns <- liftIO $ mapM canonicalizePath ccfns+ let serfnmn = Set.fromList $ map Right mns ++ map Left cfns+ opts <- targetGhcOptions crdl serfnmn+ let opts' = opts ++ ["-O0"] ++ ghcUserOptions++ gmVomit+ "session-ghc-options"+ (text "Initializing GHC session with following options")+ (intercalate " " $ map (("\""++) . (++"\"")) opts')++ initSession opts' $+ setModeSimple >>> setEmptyLogger >>> mdf++ let rfns = map (makeRelative $ cradleRootDir crdl) cfns++ unGmlT $ wrapper $ do+ loadTargets (map moduleNameString mns ++ rfns)+ action++targetGhcOptions :: forall m. IOish m+ => Cradle+ -> Set (Either FilePath ModuleName)+ -> GhcModT m [GHCOption]+targetGhcOptions crdl sefnmn = do+ when (Set.null sefnmn) $ error "targetGhcOptions: no targets given"++ case cradleProjectType crdl of+ CabalProject -> cabalOpts crdl+ _ -> sandboxOpts crdl+ where+ zipMap f l = l `zip` (f `map` l)++ cabalOpts :: Cradle -> GhcModT m [String]+ cabalOpts Cradle{..} = do+ mcs <- cabalResolvedComponents++ let mdlcs = moduleComponents mcs `zipMap` Set.toList sefnmn+ candidates = findCandidates $ map snd mdlcs++ let noCandidates = Set.null candidates+ noModuleHasAnyAssignment = all (Set.null . snd) mdlcs++ if noCandidates && noModuleHasAnyAssignment+ then do+ -- First component should be ChLibName, if no lib will take lexically first exe.+ let cns = filter (/= ChSetupHsName) $ Map.keys mcs++ gmLog GmWarning "" $ strDoc $ "Could not find a component assignment, falling back to picking library component in cabal file."+ return $ gmcGhcOpts $ fromJust $ Map.lookup (head cns) mcs+ else do+ when noCandidates $+ throwError $ GMECabalCompAssignment mdlcs++ let cn = pickComponent candidates+ return $ gmcGhcOpts $ fromJust $ Map.lookup cn mcs++resolvedComponentsCache :: IOish m => Cached (GhcModT m) GhcModState+ [GmComponent 'GMCRaw (Set.Set ModulePath)]+ (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath)))+resolvedComponentsCache = Cached {+ cacheLens = Just (lGmcResolvedComponents . lGmCaches),+ cacheFile = resolvedComponentsCacheFile,+ cachedAction = \tcfs comps ma -> do+ Cradle {..} <- cradle+ let iifsM = invalidatingInputFiles tcfs+ mums :: Maybe [Either FilePath ModuleName]+ mums =+ case iifsM of+ Nothing -> Nothing+ Just iifs ->+ let+ filterOutSetupCfg =+ filter (/= cradleRootDir </> setupConfigPath)+ changedFiles = filterOutSetupCfg iifs+ in if null changedFiles+ then Nothing+ else Just $ map Left changedFiles+ setupChanged = maybe False+ (elem $ cradleRootDir </> setupConfigPath)+ iifsM+ case (setupChanged, ma) of+ (False, Just mcs) -> gmsGet >>= \s -> gmsPut s { gmComponents = mcs }+ _ -> return ()++ let mdesc (Left f) = "file:" ++ f+ mdesc (Right mn) = "module:" ++ moduleNameString mn++ changed = map (text . mdesc) $ Foldable.concat mums+ changedDoc | [] <- changed = text "none"+ | otherwise = sep changed++ gmLog GmDebug "resolvedComponentsCache" $+ text "files changed" <+>: changedDoc++ mcs <- resolveGmComponents mums comps+ return (setupConfigPath:flatten mcs , mcs)+ }++ where+ flatten :: Map.Map ChComponentName (GmComponent t (Set.Set ModulePath))+ -> [FilePath]+ flatten = Map.elems+ >>> map (gmcHomeModuleGraph >>> gmgGraph+ >>> Map.elems+ >>> map (Set.map mpPath)+ >>> Set.unions+ )+ >>> Set.unions+ >>> Set.toList++moduleComponents :: Map ChComponentName (GmComponent t (Set ModulePath))+ -> Either FilePath ModuleName+ -> Set ChComponentName+moduleComponents m efnmn =+ foldr' Set.empty m $ \c s ->+ let+ memb =+ case efnmn of+ Left fn -> fn `Set.member` Set.map mpPath (smp c)+ Right mn -> mn `Set.member` Set.map mpModule (smp c)+ in if memb+ then Set.insert (gmcName c) s+ else s+ where+ smp c = Map.keysSet $ gmgGraph $ gmcHomeModuleGraph c++ foldr' b as f = Map.foldr f b as+++findCandidates :: [Set ChComponentName] -> Set ChComponentName+findCandidates [] = Set.empty+findCandidates scns = foldl1 Set.intersection scns++pickComponent :: Set ChComponentName -> ChComponentName+pickComponent scn = Set.findMin scn++packageGhcOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m)+ => m [GHCOption]+packageGhcOptions = do+ crdl <- cradle+ case cradleProjectType crdl of+ CabalProject -> getGhcMergedPkgOptions+ _ -> sandboxOpts crdl++-- also works for plain projects!+sandboxOpts :: MonadIO m => Cradle -> m [String]+sandboxOpts crdl = do+ pkgDbStack <- liftIO $ getSandboxPackageDbStack $ cradleRootDir crdl+ let pkgOpts = ghcDbStackOpts pkgDbStack+ return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"]+ where+ (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl)++ getSandboxPackageDbStack :: FilePath+ -- ^ Project Directory (where the cabal.sandbox.config+ -- file would be if it exists)+ -> IO [GhcPkgDb]+ getSandboxPackageDbStack cdir =+ ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb cdir++resolveGmComponent :: (IOish m, GmLog m, GmEnv m)+ => Maybe [CompilationUnit] -- ^ Updated modules+ -> GmComponent 'GMCRaw (Set ModulePath)+ -> m (GmComponent 'GMCResolved (Set ModulePath))+resolveGmComponent mums c@GmComponent {..} = do+ withLightHscEnv ghcOpts $ \env -> do+ let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs+ let mg = gmcHomeModuleGraph+ let simp = gmcEntrypoints+ sump <- case mums of+ Nothing -> return simp+ Just ums ->+ Set.fromList . catMaybes <$>+ mapM (resolveModule env srcDirs) ums++ mg' <- canonicalizeModuleGraph =<< updateHomeModuleGraph env mg simp sump++ return $ c { gmcEntrypoints = simp, gmcHomeModuleGraph = mg' }++ where ghcOpts = concat [+ gmcGhcSrcOpts,+ gmcGhcLangOpts,+ [ "-optP-include", "-optP" ++ macrosHeaderPath ]+ ]++resolveEntrypoint :: (IOish m, GmEnv m, GmLog m)+ => Cradle+ -> GmComponent 'GMCRaw ChEntrypoint+ -> m (GmComponent 'GMCRaw (Set ModulePath))+resolveEntrypoint Cradle {..} c@GmComponent {..} = do+ withLightHscEnv gmcGhcSrcOpts $ \env -> do+ let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs+ eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints+ rms <- resolveModule env srcDirs `mapM` eps+ return c { gmcEntrypoints = Set.fromList $ catMaybes rms }++-- TODO: remember that he file from `main-is:` is always module `Main` and let+-- ghc do the warning about it. Right now we run that module through+-- resolveModule like any other+resolveChEntrypoints :: FilePath -> ChEntrypoint -> IO [CompilationUnit]+resolveChEntrypoints _ (ChLibEntrypoint em om) =+ return $ map (Right . chModToMod) (em ++ om)++resolveChEntrypoints _ (ChExeEntrypoint main om) =+ return $ [Left main] ++ map (Right . chModToMod) om++resolveChEntrypoints srcDir ChSetupEntrypoint = do+ shs <- doesFileExist (srcDir </> "Setup.hs")+ slhs <- doesFileExist (srcDir </> "Setup.lhs")+ return $ case (shs, slhs) of+ (True, _) -> [Left "Setup.hs"]+ (_, True) -> [Left "Setup.lhs"]+ (False, False) -> []++chModToMod :: ChModuleName -> ModuleName+chModToMod (ChModuleName mn) = mkModuleName mn++resolveModule :: (MonadIO m, GmEnv m, GmLog m) =>+ HscEnv -> [FilePath] -> CompilationUnit -> m (Maybe ModulePath)+resolveModule env _srcDirs (Right mn) =+ liftIO $ traverse canonicalizeModulePath =<< findModulePath env mn+resolveModule env srcDirs (Left fn') = do+ mfn <- liftIO $ findFile' srcDirs fn'+ case mfn of+ Nothing -> return Nothing+ Just fn'' -> do+ fn <- liftIO $ canonicalizePath fn''+ emn <- liftIO $ fileModuleName env fn+ case emn of+ Left errs -> do+ gmLog GmWarning ("resolveModule " ++ show fn) $+ Monoid.mempty $+$ (vcat $ map text errs)+ return Nothing -- TODO: should expose these errors otherwise+ -- modules with preprocessor/parse errors are+ -- going to be missing+ Right mmn -> return $ Just $+ case mmn of+ Nothing -> mkMainModulePath fn+ Just mn -> ModulePath mn fn+ where+ -- needed for ghc 7.4+ findFile' dirs file =+ getFirst . mconcat <$> mapM (fmap First . mightExist . (</>file)) dirs++ -- fileModuleName fn (dir:dirs)+ -- | makeRelative dir fn /= fn++type CompilationUnit = Either FilePath ModuleName++resolveGmComponents :: (IOish m, GmState m, GmLog m, GmEnv m)+ => Maybe [CompilationUnit]+ -- ^ Updated modules+ -> [GmComponent 'GMCRaw (Set ModulePath)]+ -> m (Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath)))+resolveGmComponents mumns cs = do+ s <- gmsGet+ m' <- foldrM' (gmComponents s) cs $ \c m -> do+ case Map.lookup (gmcName c) m of+ Nothing -> insertUpdated m c+ Just c' -> if same gmcRawEntrypoints c c' && same gmcGhcSrcOpts c c'+ then return m+ else insertUpdated m c+ gmsPut s { gmComponents = m' }+ return m'++ where+ foldrM' b fa f = foldrM f b fa+ insertUpdated m c = do+ rc <- resolveGmComponent mumns c+ return $ Map.insert (gmcName rc) rc m++ same :: Eq b+ => (forall t a. GmComponent t a -> b)+ -> GmComponent u c -> GmComponent v d -> Bool+ same f a b = (f a) == (f b)+ -- | Set the files as targets and load them.-setTargetFiles :: IOish m => [FilePath] -> GhcModT m ()-setTargetFiles files = do- targets <- forM files $ \file -> G.guessTarget file Nothing- G.setTargets targets+loadTargets :: IOish m => [String] -> GmlT m ()+loadTargets filesOrModules = do+ gmLog GmDebug "loadTargets" $+ text "Loading" <+>: fsep (map text filesOrModules)++ targets <- forM filesOrModules (flip guessTarget Nothing)+ setTargets targets+ mode <- getCompilerMode- if mode == Intelligent then- loadTargets Intelligent+ if mode == Intelligent+ then loadTargets' Intelligent else do- mdls <- G.depanal [] False+ mdls <- depanal [] False let fallback = needsFallback mdls if fallback then do resetTargets targets setIntelligent- loadTargets Intelligent+ gmLog GmInfo "loadTargets" $+ text "Target needs interpeter, switching to LinkInMemory/HscInterpreted. Perfectly normal if anything is using TemplateHaskell, QuasiQuotes or PatternSynonyms."+ loadTargets' Intelligent else- loadTargets Simple+ loadTargets' Simple where- loadTargets Simple = do- -- Reporting error A and error B- void $ G.load LoadAllTargets- mss <- filter (\x -> G.ms_hspp_file x `elem` files) <$> G.getModuleGraph- -- Reporting error B and error C- mapM_ (G.parseModule >=> G.typecheckModule >=> G.desugarModule) mss- -- Error B duplicates. But we cannot ignore both error reportings,- -- sigh. So, the logger makes log messages unique by itself.- loadTargets Intelligent = do- df <- G.getSessionDynFlags- void $ G.setSessionDynFlags (setModeIntelligent df)- void $ G.load LoadAllTargets+ loadTargets' Simple = do+ void $ load LoadAllTargets+ mapM_ (parseModule >=> typecheckModule >=> desugarModule) =<< getModuleGraph++ loadTargets' Intelligent = do+ df <- getSessionDynFlags+ void $ setSessionDynFlags (setModeIntelligent df)+ void $ load LoadAllTargets+ resetTargets targets = do- G.setTargets []- void $ G.load LoadAllTargets- G.setTargets targets+ setTargets []+ void $ load LoadAllTargets+ setTargets targets+ setIntelligent = do- newdf <- setModeIntelligent <$> G.getSessionDynFlags- void $ G.setSessionDynFlags newdf+ newdf <- setModeIntelligent <$> getSessionDynFlags+ void $ setSessionDynFlags newdf setCompilerMode Intelligent -needsFallback :: G.ModuleGraph -> Bool+needsFallback :: ModuleGraph -> Bool needsFallback = any $ \ms ->- let df = G.ms_hspp_opts ms in+ let df = ms_hspp_opts ms in Opt_TemplateHaskell `xopt` df || Opt_QuasiQuotes `xopt` df #if __GLASGOW_HASKELL__ >= 708 || (Opt_PatternSynonyms `xopt` df) #endif++cabalResolvedComponents :: (IOish m) =>+ GhcModT m (Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath)))+cabalResolvedComponents = do+ crdl@(Cradle{..}) <- cradle+ comps <- mapM (resolveEntrypoint crdl) =<< getComponents+ cached cradleRootDir resolvedComponentsCache comps
Language/Haskell/GhcMod/Types.hs view
@@ -1,13 +1,46 @@-module Language.Haskell.GhcMod.Types where+{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, DeriveGeneric,+ StandaloneDeriving, DefaultSignatures, FlexibleInstances, TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-}+module Language.Haskell.GhcMod.Types (+ module Language.Haskell.GhcMod.Types+ , ModuleName+ , mkModuleName+ , moduleNameString+ ) where import Control.Monad.Trans.Control (MonadBaseControl)+import Control.Monad.Error (Error(..))+import qualified Control.Monad.IO.Class as MTL+import Control.Exception (Exception)+import Control.Applicative+import Control.Concurrent+import Control.Monad+import Data.Serialize+import Data.Version import Data.List (intercalate)-import qualified Data.Map as M+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Monoid+import Data.Maybe+import Data.Typeable (Typeable)+import Data.IORef+import Data.Label.Derive+import Distribution.Helper import Exception (ExceptionMonad)-import MonadUtils (MonadIO)-+#if __GLASGOW_HASKELL__ < 708+import qualified MonadUtils as GHC (MonadIO(..))+#endif+import GHC (ModuleName, moduleNameString, mkModuleName)+import HscTypes (HscEnv) import PackageConfig (PackageConfig)+import GHC.Generics+import Text.PrettyPrint (Doc)+import Prelude +import Language.Haskell.GhcMod.Caching.Types+ -- | A constraint alias (-XConstraintKinds) to make functions dealing with -- 'GhcModT' somewhat cleaner. --@@ -16,6 +49,18 @@ -- the exported API so users have the option to use a custom inner monad. type IOish m = (Functor m, MonadIO m, MonadBaseControl IO m, ExceptionMonad m) ++-- MonadUtils of GHC 7.6 or earlier defines its own MonadIO.+-- MonadUtils of GHC 7.8 or later imports MonadIO in Monad.Control.IO.Class.+#if __GLASGOW_HASKELL__ < 708+type MonadIOC m = (GHC.MonadIO m, MTL.MonadIO m)+#else+type MonadIOC m = (MTL.MonadIO m)+#endif++class MonadIOC m => MonadIO m where+ liftIO :: IO a -> m a+ -- | Output style. data OutputStyle = LispStyle -- ^ S expression style. | PlainStyle -- ^ Plain textstyle.@@ -28,8 +73,15 @@ outputStyle :: OutputStyle -- | Line separator string. , lineSeparator :: LineSeparator+ -- | Stdout/err line multiplexing using prefix encoding. @fst@ is stdout,+ -- @snd@ is stderr prefix.+ , linePrefix :: Maybe (String, String)+ -- | Verbosity+ , logLevel :: GmLogLevel -- | @ghc@ program name. , ghcProgram :: FilePath+ -- | @ghc-pkg@ program name.+ , ghcPkgProgram :: FilePath -- | @cabal@ program name. , cabalProgram :: FilePath -- | GHC command line options set on the @ghc-mod@ command line@@ -43,44 +95,114 @@ , hlintOpts :: [String] } deriving (Show) - -- | A default 'Options'. defaultOptions :: Options defaultOptions = Options {- outputStyle = PlainStyle- , hlintOpts = []- , ghcProgram = "ghc"- , cabalProgram = "cabal"- , ghcUserOptions= []- , operators = False- , detailed = False- , qualified = False- , lineSeparator = LineSeparator "\0"+ outputStyle = PlainStyle+ , lineSeparator = LineSeparator "\0"+ , linePrefix = Nothing+ , logLevel = GmWarning+ , ghcProgram = "ghc"+ , ghcPkgProgram = "ghc-pkg"+ , cabalProgram = "cabal"+ , ghcUserOptions = []+ , operators = False+ , detailed = False+ , qualified = False+ , hlintOpts = [] } ---------------------------------------------------------------- +data ProjectType = CabalProject | SandboxProject | PlainProject+ deriving (Eq, Show)+ -- | The environment where this library is used. data Cradle = Cradle {+ cradleProjectType:: ProjectType -- | The directory where this library is executed.- cradleCurrentDir :: FilePath+ , cradleCurrentDir :: FilePath -- | The project root directory. , cradleRootDir :: FilePath -- | Per-Project temporary directory , cradleTempDir :: FilePath -- | The file name of the found cabal file. , cradleCabalFile :: Maybe FilePath- -- | Package database stack- , cradlePkgDbStack :: [GhcPkgDb] } deriving (Eq, Show) ++data GmStream = GmOut | GmErr+ deriving (Show)++data GmLineType = GmTerminated | GmPartial+ deriving (Show)++data GmLines a = GmLines GmLineType a+ deriving (Show, Functor)++unGmLine :: GmLines a -> a+unGmLine (GmLines _ s) = s++data GmOutput = GmOutputStdio+ | GmOutputChan (Chan (GmStream, GmLines String))++data GhcModEnv = GhcModEnv {+ gmOptions :: Options+ , gmCradle :: Cradle+ , gmOutput :: GmOutput+ }++data GhcModLog = GhcModLog {+ gmLogLevel :: Maybe GmLogLevel,+ gmLogVomitDump :: Last Bool,+ gmLogMessages :: [(GmLogLevel, String, Doc)]+ } deriving (Show)++instance Monoid GhcModLog where+ mempty = GhcModLog (Just GmPanic) (Last Nothing) mempty+ GhcModLog ml vd ls `mappend` GhcModLog ml' vd' ls' =+ GhcModLog (ml' `mplus` ml) (vd `mappend` vd') (ls `mappend` ls')++data GmGhcSession = GmGhcSession {+ gmgsOptions :: ![GHCOption],+ gmgsSession :: !(IORef HscEnv)+ }++data GhcModCaches = GhcModCaches {+ gmcPackageDbStack :: CacheContents ChCacheData [GhcPkgDb]+ , gmcMergedPkgOptions :: CacheContents ChCacheData [GHCOption]+ , gmcComponents :: CacheContents ChCacheData [GmComponent 'GMCRaw ChEntrypoint]+ , gmcResolvedComponents :: CacheContents+ [GmComponent 'GMCRaw (Set.Set ModulePath)]+ (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath)))+ }++data GhcModState = GhcModState {+ gmGhcSession :: !(Maybe GmGhcSession)+ , gmComponents :: !(Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath)))+ , gmCompilerMode :: !CompilerMode+ , gmCaches :: !GhcModCaches+ }++data CompilerMode = Simple | Intelligent deriving (Eq,Show,Read)++defaultGhcModState :: GhcModState+defaultGhcModState =+ GhcModState n Map.empty Simple (GhcModCaches n n n n)+ where n = Nothing+ ---------------------------------------------------------------- -- | GHC package database flags.-data GhcPkgDb = GlobalDb | UserDb | PackageDb String deriving (Eq, Show)+data GhcPkgDb = GlobalDb+ | UserDb+ | PackageDb String+ deriving (Eq, Show, Generic) +instance Serialize GhcPkgDb+ -- | A single GHC command line option.-type GHCOption = String+type GHCOption = String -- | An include directory for modules. type IncludeDir = FilePath@@ -89,44 +211,175 @@ type PackageBaseName = String -- | A package version.-type PackageVersion = String+type PackageVersion = String -- | A package id.-type PackageId = String+type PackageId = String -- | A package's name, verson and id.-type Package = (PackageBaseName, PackageVersion, PackageId)+type Package = (PackageBaseName, PackageVersion, PackageId) pkgName :: Package -> PackageBaseName-pkgName (n,_,_) = n+pkgName (n, _, _) = n pkgVer :: Package -> PackageVersion-pkgVer (_,v,_) = v+pkgVer (_, v, _) = v pkgId :: Package -> PackageId-pkgId (_,_,i) = i+pkgId (_, _, i) = i showPkg :: Package -> String-showPkg (n,v,_) = intercalate "-" [n,v]+showPkg (n, v, _) = intercalate "-" [n, v] showPkgId :: Package -> String-showPkgId (n,v,i) = intercalate "-" [n,v,i]---- | Collection of packages-type PkgDb = (M.Map Package PackageConfig)+showPkgId (n, v, i) = intercalate "-" [n, v, i] -- | Haskell expression.-type Expression = String+newtype Expression = Expression { getExpression :: String }+ deriving (Show, Eq, Ord) -- | Module name.-type ModuleString = String+newtype ModuleString = ModuleString { getModuleString :: String }+ deriving (Show, Read, Eq, Ord) --- | A Module-type Module = [String]+data GmLogLevel =+ GmSilent+ | GmPanic+ | GmException+ | GmError+ | GmWarning+ | GmInfo+ | GmDebug+ | GmVomit+ deriving (Eq, Ord, Enum, Bounded, Show, Read) --- | Option information for GHC-data CompilerOptions = CompilerOptions {- ghcOptions :: [GHCOption] -- ^ Command line options- , includeDirs :: [IncludeDir] -- ^ Include directories for modules- , depPackages :: [Package] -- ^ Dependent package names- } deriving (Eq, Show)+-- | Collection of packages+type PkgDb = (Map Package PackageConfig)++data GmModuleGraph = GmModuleGraph {+ gmgGraph :: Map ModulePath (Set ModulePath)+ } deriving (Eq, Ord, Show, Read, Generic, Typeable)++instance Serialize GmModuleGraph where+ put GmModuleGraph {..} = put (mpim, graph)+ where+ mpim :: Map ModulePath Integer+ mpim = Map.fromList $ Map.keys gmgGraph `zip` [0..]+ graph :: Map Integer (Set Integer)+ graph = Map.map (Set.map mpToInt) $ Map.mapKeys mpToInt gmgGraph+ mpToInt :: ModulePath -> Integer+ mpToInt mp = fromJust $ Map.lookup mp mpim++ get = do+ (mpim :: Map ModulePath Integer, graph :: Map Integer (Set Integer)) <- get+ let impm = swapMap mpim+ intToMp i = fromJust $ Map.lookup i impm+ mpGraph :: Map ModulePath (Set ModulePath)+ mpGraph = Map.map (Set.map intToMp) $ Map.mapKeys intToMp graph+ return $ GmModuleGraph mpGraph+ where+ swapMap :: (Ord k, Ord v) => Map k v -> Map v k+ swapMap = Map.fromList . map (\(x, y) -> (y, x)) . Map.toList++instance Monoid GmModuleGraph where+ mempty = GmModuleGraph mempty+ mappend (GmModuleGraph a) (GmModuleGraph a') =+ GmModuleGraph (Map.unionWith Set.union a a')++data GmComponentType = GMCRaw+ | GMCResolved+data GmComponent (t :: GmComponentType) eps = GmComponent {+ gmcHomeModuleGraph :: GmModuleGraph+ , gmcName :: ChComponentName+ , gmcGhcOpts :: [GHCOption]+ , gmcGhcPkgOpts :: [GHCOption]+ , gmcGhcSrcOpts :: [GHCOption]+ , gmcGhcLangOpts :: [GHCOption]+ , gmcRawEntrypoints :: ChEntrypoint+ , gmcEntrypoints :: eps+ , gmcSourceDirs :: [FilePath]+ } deriving (Eq, Ord, Show, Read, Generic, Functor)++instance Serialize eps => Serialize (GmComponent t eps)++data ModulePath = ModulePath { mpModule :: ModuleName, mpPath :: FilePath }+ deriving (Eq, Ord, Show, Read, Generic, Typeable)+instance Serialize ModulePath++instance Serialize ModuleName where+ get = mkModuleName <$> get+ put mn = put (moduleNameString mn)++instance Show ModuleName where+ show mn = "ModuleName " ++ show (moduleNameString mn)++instance Read ModuleName where+ readsPrec d =+ readParen+ (d > app_prec)+ (\r' -> [ (mkModuleName m, t)+ | ("ModuleName", s) <- lex r'+ , (m, t) <- readsPrec (app_prec + 1) s+ ])+ where+ app_prec = 10++data GhcModError+ = GMENoMsg+ -- ^ Unknown error++ | GMEString String+ -- ^ Some Error with a message. These are produced mostly by+ -- 'fail' calls on GhcModT.++ | GMECabalConfigure GhcModError+ -- ^ Configuring a cabal project failed.++ | GMECabalFlags GhcModError+ -- ^ Retrieval of the cabal configuration flags failed.++ | GMECabalComponent ChComponentName+ -- ^ Cabal component could not be found++ | GMECabalCompAssignment [(Either FilePath ModuleName, Set ChComponentName)]+ -- ^ Could not find a consistent component assignment for modules++ | GMEProcess String [String] (Either (String, String, Int) GhcModError)+ -- ^ Launching an operating system process failed. Fields in+ -- order: command, arguments, (stdout, stderr, exitcode)++ | GMENoCabalFile+ -- ^ No cabal file found.++ | GMETooManyCabalFiles [FilePath]+ -- ^ Too many cabal files found.++ | GMECabalStateFile GMConfigStateFileError+ -- ^ Reading Cabal's state configuration file falied somehow.+ deriving (Eq,Show,Typeable)++instance Error GhcModError where+ noMsg = GMENoMsg+ strMsg = GMEString++instance Exception GhcModError++data GMConfigStateFileError+ = GMConfigStateFileNoHeader+ | GMConfigStateFileBadHeader+ | GMConfigStateFileNoParse+ | GMConfigStateFileMissing+-- | GMConfigStateFileBadVersion PackageIdentifier PackageIdentifier (Either ConfigStateFileError LocalBuildInfo)+ deriving (Eq, Show, Read, Typeable)+++deriving instance Generic Version+instance Serialize Version++instance Serialize Programs+instance Serialize ChModuleName+instance Serialize ChComponentName+instance Serialize ChEntrypoint++mkLabel ''GhcModCaches+mkLabel ''GhcModState
Language/Haskell/GhcMod/Utils.hs view
@@ -1,94 +1,159 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.+ {-# LANGUAGE CPP #-}-module Language.Haskell.GhcMod.Utils where+{-# LANGUAGE DoAndIfThenElse #-} -import Control.Arrow+module Language.Haskell.GhcMod.Utils (+ module Language.Haskell.GhcMod.Utils+ , module Utils+ , readProcess+ ) where++import Control.Applicative import Data.Char+import Exception import Language.Haskell.GhcMod.Error-import MonadUtils (MonadIO, liftIO)-import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist)-import System.Exit (ExitCode(..))-import System.Process (readProcessWithExitCode)-import System.Directory (getTemporaryDirectory)-import System.FilePath (splitDrive, pathSeparators)-import System.IO.Temp (createTempDirectory)-#ifndef SPEC-import Control.Applicative ((<$>))+import Language.Haskell.GhcMod.Monad.Types+import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist,+ getTemporaryDirectory, canonicalizePath) import System.Environment-import System.FilePath ((</>),takeDirectory)-#endif+import System.FilePath (splitDrive, takeDirectory, takeFileName, pathSeparators,+ (</>))+import System.IO.Temp (createTempDirectory)+import System.Process (readProcess)+import Text.Printf +import Paths_ghc_mod (getLibexecDir)+import Utils+import Prelude+ -- dropWhileEnd is not provided prior to base 4.5.0.0. dropWhileEnd :: (a -> Bool) -> [a] -> [a] dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) [] -extractParens :: String -> String-extractParens str = extractParens' str 0- where- extractParens' :: String -> Int -> String- extractParens' [] _ = []- extractParens' (s:ss) level- | s `elem` "([{" = s : extractParens' ss (level+1)- | level == 0 = extractParens' ss 0- | s `elem` "}])" && level == 1 = [s]- | s `elem` "}])" = s : extractParens' ss (level-1)- | otherwise = s : extractParens' ss level--readProcess' :: (MonadIO m, MonadError GhcModError m)- => String- -> [String]- -> m String-readProcess' cmd opts = do- (rv,output,err) <- liftIO (readProcessWithExitCode cmd opts "")- `modifyError'` GMEProcess ([cmd] ++ opts)- case rv of- ExitFailure val -> do- throwError $ GMEProcess ([cmd] ++ opts) $ strMsg $- cmd ++ " " ++ unwords opts ++ " (exit " ++ show val ++ ")"- ++ "\n" ++ err- ExitSuccess ->- return output- withDirectory_ :: (MonadIO m, ExceptionMonad m) => FilePath -> m a -> m a withDirectory_ dir action =- gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory)- (\_ -> liftIO (setCurrentDirectory dir) >> action)+ gbracket+ (liftIO getCurrentDirectory)+ (liftIO . setCurrentDirectory)+ (\_ -> liftIO (setCurrentDirectory dir) >> action) uniqTempDirName :: FilePath -> FilePath-uniqTempDirName dir = ("ghc-mod"++) $ uncurry (++)- $ map escapeDriveChar *** map escapePathChar- $ splitDrive dir- where+uniqTempDirName dir =+ "ghc-mod" ++ map escapeDriveChar drive ++ map escapePathChar path+ where+ (drive, path) = splitDrive dir+ escapeDriveChar :: Char -> Char escapeDriveChar c- | isAlphaNum c = c- | otherwise = '-'-+ | isAlphaNum c = c+ | otherwise = '-'+ escapePathChar :: Char -> Char escapePathChar c- | c `elem` pathSeparators = '-'- | otherwise = c+ | c `elem` pathSeparators = '-'+ | otherwise = c newTempDir :: FilePath -> IO FilePath newTempDir dir =- flip createTempDirectory (uniqTempDirName dir) =<< getTemporaryDirectory+ flip createTempDirectory (uniqTempDirName dir) =<< getTemporaryDirectory -mightExist :: FilePath -> IO (Maybe FilePath)-mightExist f = do- exists <- doesFileExist f- return $ if exists then (Just f) else (Nothing)+whenM :: Monad m => m Bool -> m () -> m ()+whenM mb ma = mb >>= flip when ma -- | Returns the path to the currently running ghc-mod executable. With ghc<7.6 -- this is a guess but >=7.6 uses 'getExecutablePath'. ghcModExecutable :: IO FilePath #ifndef SPEC ghcModExecutable = do- dir <- getExecutablePath'- return $ dir </> "ghc-mod"- where- getExecutablePath' :: IO FilePath-# if __GLASGOW_HASKELL__ >= 706- getExecutablePath' = takeDirectory <$> getExecutablePath-# else- getExecutablePath' = return ""-# endif+ dir <- takeDirectory <$> getExecutablePath'+ return $ (if dir == "." then "" else dir) </> "ghc-mod" #else-ghcModExecutable = return "dist/build/ghc-mod/ghc-mod"+ghcModExecutable = fmap (</> "dist/build/ghc-mod/ghc-mod") getCurrentDirectory #endif++findLibexecExe :: String -> IO FilePath+findLibexecExe "cabal-helper-wrapper" = do+ libexecdir <- getLibexecDir+ let exeName = "cabal-helper-wrapper"+ exe = libexecdir </> exeName++ exists <- doesFileExist exe++ if exists+ then return exe+ else do+ mdir <- tryFindGhcModTreeDataDir+ case mdir of+ Nothing ->+ error $ libexecNotExitsError exeName libexecdir+ Just dir ->+ return $ dir </> "dist" </> "build" </> exeName </> exeName+findLibexecExe exe = error $ "findLibexecExe: Unknown executable: " ++ exe++libexecNotExitsError :: String -> FilePath -> String+libexecNotExitsError exe dir = printf+ ( "Could not find $libexecdir/%s\n"+ ++"\n"+ ++"If you are a developer set the environment variable `ghc_mod_libexecdir'\n"+ ++"to override $libexecdir[1] the following will work in the ghc-mod tree:\n"+ ++"\n"+ ++" $ export ghc_mod_libexecdir=$PWD/dist/build/%s\n"+ ++"\n"+ ++"[1]: %s\n"+ ++"\n"+ ++"If you don't know what I'm talking about something went wrong with your\n"+ ++"installation. Please report this problem here:\n"+ ++"\n"+ ++" https://github.com/kazu-yamamoto/ghc-mod/issues") exe exe dir++tryFindGhcModTreeLibexecDir :: IO (Maybe FilePath)+tryFindGhcModTreeLibexecDir = do+ exe <- getExecutablePath'+ dir <- case takeFileName exe of+ "ghc" -> getCurrentDirectory -- we're probably in ghci; try CWD+ _ -> return $ (!!4) $ iterate takeDirectory exe+ exists <- doesFileExist $ dir </> "ghc-mod.cabal"+ return $ if exists+ then Just dir+ else Nothing++tryFindGhcModTreeDataDir :: IO (Maybe FilePath)+tryFindGhcModTreeDataDir = do+ dir <- (!!4) . iterate takeDirectory <$> getExecutablePath'+ exists <- doesFileExist $ dir </> "ghc-mod.cabal"+ return $ if exists+ then Just dir+ else Nothing++readLibExecProcess' :: (MonadIO m, ExceptionMonad m)+ => String -> [String] -> m String+readLibExecProcess' cmd args = do+ exe <- liftIO $ findLibexecExe cmd+ liftIO $ readProcess exe args ""++getExecutablePath' :: IO FilePath+#if __GLASGOW_HASKELL__ >= 706+getExecutablePath' = getExecutablePath+#else+getExecutablePath' = getProgName+#endif++canonFilePath :: FilePath -> IO FilePath+canonFilePath f = do+ p <- canonicalizePath f+ e <- doesFileExist p+ when (not e) $ error $ "canonFilePath: not a file: " ++ p+ return p
Language/Haskell/GhcMod/World.hs view
@@ -1,89 +1,52 @@-{-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.World where-{-(- , World- , getCurrentWorld- , isWorldChanged- ) where--} import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Types+import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Utils -import Control.Applicative (pure,(<$>),(<*>))+import Control.Applicative import Data.Maybe-import Data.Traversable (traverse)-import System.Directory (getModificationTime)+import Data.Traversable hiding (mapM) import System.FilePath ((</>)) import GHC.Paths (libdir)--#if __GLASGOW_HASKELL__ <= 704-import System.Time (ClockTime)-#else-import Data.Time (UTCTime)-#endif---#if __GLASGOW_HASKELL__ <= 704-type ModTime = ClockTime-#else-type ModTime = UTCTime-#endif--data TimedFile = TimedFile FilePath ModTime deriving (Eq, Show)--instance Ord TimedFile where- compare (TimedFile _ a) (TimedFile _ b) = compare a b--timeFile :: FilePath -> IO TimedFile-timeFile f = TimedFile <$> pure f <*> getModificationTime f+import Prelude data World = World { worldPackageCaches :: [TimedFile] , worldCabalFile :: Maybe TimedFile , worldCabalConfig :: Maybe TimedFile+ , worldSymbolCache :: Maybe TimedFile } deriving (Eq, Show) -timedPackageCache :: Cradle -> IO [TimedFile]-timedPackageCache crdl = do- fs <- mapM mightExist . map (</> packageCache)- =<< getPackageCachePaths libdir crdl- timeFile `mapM` catMaybes fs+timedPackageCaches :: IOish m => GhcModT m [TimedFile]+timedPackageCaches = do+ fs <- mapM (liftIO . mightExist) . map (</> packageCache)+ =<< getPackageCachePaths libdir+ (liftIO . timeFile) `mapM` catMaybes fs -getCurrentWorld :: Cradle -> IO World-getCurrentWorld crdl = do- pkgCaches <- timedPackageCache crdl- mCabalFile <- timeFile `traverse` cradleCabalFile crdl- mSetupConfig <- mightExist (setupConfigFile crdl)- mCabalConfig <- timeFile `traverse` mSetupConfig+getCurrentWorld :: IOish m => GhcModT m World+getCurrentWorld = do+ crdl <- cradle+ pkgCaches <- timedPackageCaches+ mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl+ mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl)+ mSymbolCache <- liftIO $ timeMaybe (symbolCache crdl) return World { worldPackageCaches = pkgCaches , worldCabalFile = mCabalFile , worldCabalConfig = mCabalConfig+ , worldSymbolCache = mSymbolCache } -didWorldChange :: World -> Cradle -> IO Bool-didWorldChange world crdl = do- (world /=) <$> getCurrentWorld crdl+didWorldChange :: IOish m => World -> GhcModT m Bool+didWorldChange world = do+ (world /=) <$> getCurrentWorld --- * Neither file exists -> should return False:--- @Nothing < Nothing = False@--- (since we don't need to @cabal configure@ when no cabal file exists.)------ * Cabal file doesn't exist (unlikely case) -> should return False--- @Just cc < Nothing = False@--- TODO: should we delete dist/setup-config?------ * dist/setup-config doesn't exist yet -> should return True:--- @Nothing < Just cf = True@------ * Both files exist--- @Just cc < Just cf = cc < cf = cc `olderThan` cf@-isSetupConfigOutOfDate :: Cradle -> IO Bool-isSetupConfigOutOfDate crdl = do- world <- getCurrentWorld crdl- return $ worldCabalConfig world < worldCabalFile world+isYoungerThanSetupConfig :: FilePath -> World -> IO Bool+isYoungerThanSetupConfig file World {..} = do+ tfile <- timeFile file+ return $ worldCabalConfig < Just tfile
+ NotCPP/Declarations.hs view
@@ -0,0 +1,164 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+{-# LANGUAGE CPP #-}+-- Using CPP so you don't have to :)+module NotCPP.Declarations where++import Control.Arrow+import Control.Applicative+import Data.Maybe+import Language.Haskell.TH.Syntax++import NotCPP.LookupValueName++nT :: Monad m => String -> m Type+cT :: Monad m => String -> m Type+nE :: Monad m => String -> m Exp+nP :: Monad m => String -> m Pat++nT str = return $ VarT (mkName str)+cT str = return $ ConT (mkName str)+nE str = return $ VarE (mkName str)+nP str = return $ VarP (mkName str)+recUpdE' :: Q Exp -> Name -> Exp -> Q Exp+recUpdE' ex name assign = do+ RecUpdE <$> ex <*> pure [(name, assign)]++lookupName' :: (NameSpace, String) -> Q (Maybe Name)+lookupName' (VarName, n) = lookupValueName n+lookupName' (DataName, n) = lookupValueName n+lookupName' (TcClsName, n) = lookupTypeName n++-- Does this even make sense?+ifelseD :: Q [Dec] -> Q [Dec] -> Q [Dec]+ifelseD if_decls' else_decls = do+ if_decls <- if_decls'+ alreadyDefined <- definedNames (boundNames `concatMap` if_decls)+ case alreadyDefined of+ [] -> if_decls'+ _ -> else_decls++ifdefelseD, ifelsedefD :: String -> Q [Dec] -> Q [Dec] -> Q [Dec]+ifelsedefD = ifdefelseD+ifdefelseD ident if_decls else_decls = do+ exists <- isJust <$> lookupValueName ident+ if exists+ then if_decls+ else else_decls++ifdefD :: String -> Q [Dec] -> Q [Dec]+ifdefD ident decls = ifdefelseD ident decls (return [])++ifndefD :: String -> Q [Dec] -> Q [Dec]+ifndefD ident decls = ifdefelseD ident (return []) decls++-- | Each of the given declarations is only spliced if the identifier it defines+-- is not defined yet.+--+-- For example:+--+-- @$(ifD [[d| someFunctionThatShouldExist x = x+1 |]]@+--+-- If @someFunctionThatShouldExist@ doesn't actually exist the definition given+-- in the splice will be the result of the splice otherwise nothing will be+-- spliced.+--+-- Currently this only works for function declarations but it can be easily+-- extended to other kinds of declarations.+ifD :: Q [Dec] -> Q [Dec]+ifD decls' = do+ decls <- decls'+ concat <$> flip mapM decls (\decl -> do+ alreadyDefined <- definedNames (boundNames decl)+ case alreadyDefined of+ [] -> return [decl]+ _ -> return [])++definedNames :: [(NameSpace, Name)] -> Q [Name]+definedNames ns = catMaybes <$> (lookupName' . second nameBase) `mapM` ns++boundNames :: Dec -> [(NameSpace, Name)]+boundNames decl =+ case decl of+ SigD n _ -> [(VarName, n)]+ FunD n _cls -> [(VarName, n)]+#if __GLASGOW_HASKELL__ >= 706+ InfixD _ n -> [(VarName, n)]+#endif+ ValD p _ _ -> map ((,) VarName) $ patNames p++ TySynD n _ _ -> [(TcClsName, n)]+ ClassD _ n _ _ _ -> [(TcClsName, n)]+ FamilyD _ n _ _ -> [(TcClsName, n)]++ DataD _ n _ ctors _ ->+ [(TcClsName, n)] ++ map ((,) TcClsName) (conNames `concatMap` ctors)++ NewtypeD _ n _ ctor _ ->+ [(TcClsName, n)] ++ map ((,) TcClsName) (conNames ctor)++ DataInstD _ _n _ ctors _ ->+ map ((,) TcClsName) (conNames `concatMap` ctors)++ NewtypeInstD _ _n _ ctor _ ->+ map ((,) TcClsName) (conNames ctor)++ InstanceD _ _ty _ ->+ error "notcpp: Instance declarations are not supported yet"+ ForeignD _ ->+ error "notcpp: Foreign declarations are not supported yet"+ PragmaD _pragma -> error "notcpp: pragmas are not supported yet"++#if __GLASGOW_HASKELL__ >= 708+ TySynInstD _n _ -> error "notcpp: TySynInstD not supported yet"+#else+ TySynInstD _n _ _ -> error "notcpp: TySynInstD not supported yet"+#endif++#if __GLASGOW_HASKELL__ >= 708+ ClosedTypeFamilyD n _ _ _ -> [(TcClsName, n)]+ RoleAnnotD _n _ -> error "notcpp: RoleAnnotD not supported yet"+#endif++conNames :: Con -> [Name]+conNames con =+ case con of+ NormalC n _ -> [n]+ RecC n _ -> [n]+ InfixC _ n _ -> [n]+ ForallC _ _ c -> conNames c++patNames :: Pat -> [Name]+patNames p'' =+ case p'' of+ LitP _ -> []+ VarP n -> [n]+ TupP ps -> patNames `concatMap` ps+ UnboxedTupP ps -> patNames `concatMap` ps+ ConP _ ps -> patNames `concatMap` ps+ InfixP p _ p' -> patNames `concatMap` [p,p']+ UInfixP p _ p' -> patNames `concatMap` [p,p']+ ParensP p -> patNames p+ TildeP p -> patNames p+ BangP p -> patNames p+ AsP n p -> n:(patNames p)+ WildP -> []+ RecP _ fps -> patNames `concatMap` map snd fps+ ListP ps -> patNames `concatMap` ps+ SigP p _ -> patNames p+ ViewP _ p -> patNames p
+ NotCPP/LookupValueName.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE TemplateHaskell #-}+-- | This module uses scope lookup techniques to either export+-- 'lookupValueName' from @Language.Haskell.TH@, or define+-- its own 'lookupValueName', which attempts to do the+-- same job with just 'reify'. This will sometimes fail, but if it+-- succeeds it will give the answer that the real function would have+-- given.+--+-- The idea is that if you use lookupValueName from this module,+-- your client code will automatically use the best available name+-- lookup mechanism. This means that e.g. 'scopeLookup' can work+-- very well on recent GHCs and less well but still somewhat+-- usefully on older GHCs.+module NotCPP.LookupValueName (+ lookupValueName+ ) where++import Language.Haskell.TH++import NotCPP.Utils++bestValueGuess :: String -> Q (Maybe Name)+bestValueGuess s = do+ mi <- maybeReify (mkName s)+ case mi of+ Nothing -> no+ Just i -> case i of+ VarI n _ _ _ -> yes n+ DataConI n _ _ _ -> yes n+ _ -> err ["unexpected info:", show i]+ where+ no = return Nothing+ yes = return . Just+ err = fail . showString "NotCPP.bestValueGuess: " . unwords++$(recover [d| lookupValueName = bestValueGuess |] $ do+ VarI _ _ _ _ <- reify (mkName "lookupValueName")+ return [])
+ NotCPP/OrphanEvasion.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE EmptyDataDecls, TemplateHaskell #-}+-- | +-- The orphan instance problem is well-known in Haskell. This module+-- by no means purports to solve the problem, but provides a workaround+-- that may be significantly less awful than the status quo in some+-- cases.+--+-- Say I think that the 'Name' type should have an 'IsString' instance.+-- But I don't control either the class or the type, so if I define the+-- instance, and then the template-haskell package defines one, my code+-- is going to break.+--+-- 'safeInstance' can help me to solve this problem:+--+-- > safeInstance ''IsString [t| Name |] [d|+-- > fromString = mkName |]+--+-- This will declare an instance only if one doesn't already exist.+-- Now anyone importing your module is guaranteed to get an instance+-- one way or the other.+--+-- This module is still highly experimental. The example given above+-- does work, but anything involving type variables or complex method+-- bodies may be less fortunate. The names of the methods are mangled+-- a bit, so using recursion to define them may not work. Define the+-- method outside the code and then use a simple binding as above.+--+-- If you use this code (successfully or unsuccessfully!), go fetch+-- the maintainer address from the cabal file and let me know!+module NotCPP.OrphanEvasion (+ MultiParams,+ safeInstance,+ safeInstance',+ ) where++import Control.Applicative++import Language.Haskell.TH+import Language.Haskell.TH.Syntax++import NotCPP.ScopeLookup++-- | An empty type used only to signify a multiparameter typeclass in+-- 'safeInstance'.+data MultiParams a++-- | Given @(forall ts. Cxt => t)@, return @(Cxt, [t])@.+-- Given @(forall ts. Cxt => 'MultiParams' (t1, t2, t3))@, return+-- @(Cxt, [t1, t2, t3])@.+--+-- This is used in 'safeInstance' to allow types to be specified more+-- easily with TH typequotes.+fromTuple :: Type -> (Cxt, [Type])+fromTuple ty = unTuple <$> case ty of+ ForallT _ cxt ty' -> (cxt, ty')+ _ -> ([], ty)+ where+ unTuple :: Type -> [Type]+ unTuple (AppT (ConT n) ta)+ | n == ''MultiParams = case unrollAppT ta of+ (TupleT{}, ts) -> ts+ _ -> [ty]+ unTuple t = [t]++-- | A helper function to unwind type application. +-- Given @TyCon t1 t2 t3@, returns @(TyCon, [t1,t2,t3])@+unrollAppT :: Type -> (Type, [Type])+unrollAppT = go []+ where+ go acc (AppT tc ta) = go (ta : acc) tc+ go acc ty = (ty, reverse acc)++-- | Left inverse to unrollAppT, equal to @'foldl' 'AppT'@+rollAppT :: Type -> [Type] -> Type+rollAppT = foldl AppT++-- | @'safeInstance'' className cxt types methods@ produces an instance+-- of the given class if and only if one doesn't already exist.+--+-- See 'safeInstance' for a simple way to construct the 'Cxt' and+-- @['Type']@ parameters.+safeInstance' :: Name -> Cxt -> [Type] -> Q [Dec] -> Q [Dec]+safeInstance' cl cxt tys inst = do+ b <- $(scopeLookups ["isInstance", "isClassInstance"]) cl tys+ if b+ then return []+ else do+ ds <- map fixInst <$> inst+ return [InstanceD cxt (rollAppT (ConT cl) tys) ds]+ where+ fixInst (FunD n cls) = FunD (fixName n) cls+ fixInst (ValD (VarP n) rhs wh) = ValD (VarP (fixName n)) rhs wh+ fixInst d = d+ fixName (Name n _) = Name n NameS++-- | 'safeInstance' is a more convenient version of 'safeInstance''+-- that takes the context and type from a @'Q' 'Type'@ with the intention+-- that it be supplied using a type-quote.+--+-- To define an instance @Show a => Show (Wrapper a)@, you'd use:+--+-- > safeInstance ''Show [t| Show a => Wrapper a |]+-- > [d| show _ = "stuff" |]+--+-- To define an instance of a multi-param type class, use the+-- 'MultiParams' type constructor with a tuple:+--+-- > safeInstance ''MonadState+-- > [t| MonadState s m => MultiParams (s, MaybeT m) |]+-- > [d| put = ... |]+safeInstance :: Name -> Q Type -> Q [Dec] -> Q [Dec]+safeInstance n qty inst = do+ (cxt, tys) <- fromTuple <$> qty+ safeInstance' n cxt tys inst
+ NotCPP/ScopeLookup.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE TemplateHaskell #-}+-- |+-- This module exports 'scopeLookup', which will find a variable or+-- value constructor for you and present it for your use. E.g. at some+-- point in the history of the acid-state package, 'openAcidState' was+-- renamed 'openLocalState'; for compatibility with both, you could+-- use:+--+-- > openState :: IO (AcidState st)+-- > openState = case $(scopeLookup "openLocalState") of+-- > Just open -> open defaultState+-- > Nothing -> case $(scopeLookup "openAcidState") of+-- > Just open -> open defaultState+-- > Nothing -> error+-- > "openState: runtime name resolution has its drawbacks :/"+--+-- Or, for this specific case, you can use 'scopeLookups':+--+-- > openState :: IO (AcidState st)+-- > openState = open defaultState+-- > where+-- > open = $(scopeLookups ["openLocalState","openAcidState"])+--+-- Now if neither of the names are found then TH will throw a+-- compile-time error.+module NotCPP.ScopeLookup (+ scopeLookup,+ scopeLookups,+ scopeLookup',+ liftMaybe,+ recoverMaybe,+ maybeReify,+ infoToExp,+ ) where++import Control.Applicative ((<$>))++import Language.Haskell.TH (Q, Exp, recover, reify)++import NotCPP.LookupValueName+import NotCPP.Utils++-- | Produces a spliceable expression which expands to @'Just' val@ if+-- the given string refers to a value @val@ in scope, or 'Nothing'+-- otherwise.+--+-- @scopeLookup = 'fmap' 'liftMaybe' . 'scopeLookup''@+scopeLookup :: String -> Q Exp+scopeLookup = fmap liftMaybe . scopeLookup'++-- | Finds the first string in the list that names a value, and produces+-- a spliceable expression of that value, or reports a compile error if+-- it fails.+scopeLookups :: [String] -> Q Exp+scopeLookups xs = foldr+ (\s r -> maybe r return =<< scopeLookup' s)+ (fail ("scopeLookups: none found: " ++ show xs))+ xs++-- | Produces @'Just' x@ if the given string names the value @x@,+-- or 'Nothing' otherwise.+scopeLookup' :: String -> Q (Maybe Exp)+scopeLookup' s = recover (return Nothing) $ do+ Just n <- lookupValueName s+ infoToExp <$> reify n
+ NotCPP/Utils.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE TemplateHaskell #-}+module NotCPP.Utils where++import Control.Applicative ((<$>))+import Language.Haskell.TH++-- | Turns 'Nothing' into an expression representing 'Nothing', and+-- @'Just' x@ into an expression representing 'Just' applied to the+-- expression in @x@.+liftMaybe :: Maybe Exp -> Exp+liftMaybe = maybe (ConE 'Nothing) (AppE (ConE 'Just))++-- | A useful variant of 'reify' that returns 'Nothing' instead of+-- halting compilation when an error occurs (e.g. because the given+-- name was not in scope).+maybeReify :: Name -> Q (Maybe Info)+maybeReify = recoverMaybe . reify++-- | Turns a possibly-failing 'Q' action into one returning a 'Maybe'+-- value.+recoverMaybe :: Q a -> Q (Maybe a)+recoverMaybe q = recover (return Nothing) (Just <$> q)++-- | Returns @'Just' ('VarE' n)@ if the info relates to a value called+-- @n@, or 'Nothing' if it relates to a different sort of thing.+infoToExp :: Info -> Maybe Exp+infoToExp (VarI n _ _ _) = Just (VarE n)+infoToExp (DataConI n _ _ _) = Just (ConE n)+infoToExp _ = Nothing
Setup.hs view
@@ -1,2 +1,198 @@+#!/usr/bin/env runhaskell+{-# LANGUAGE RecordWildCards #-} import Distribution.Simple-main = defaultMain+import Distribution.Simple.Setup+import Distribution.Simple.Install+import Distribution.Simple.Register+import Distribution.Simple.InstallDirs as ID+import Distribution.Simple.LocalBuildInfo+import Distribution.PackageDescription++import Control.Arrow+import Control.Applicative+import Control.Monad+import Data.List+import Data.Maybe+import Data.Version+import Data.Monoid+import System.Process+import System.Exit+import System.FilePath+import Text.ParserCombinators.ReadP++import SetupCompat++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks {+ confHook = \(gpd, hbi) cf ->+ xBuildDependsLike <$> (confHook simpleUserHooks) (gpd, hbi) cf++ , instHook = inst+ , copyHook = copy++-- , postConf = sanityCheckCabalVersions+ }++xBuildDependsLike :: LocalBuildInfo -> LocalBuildInfo+xBuildDependsLike lbi =+ let+ cc = componentsConfigs lbi+ pd = localPkgDescr lbi+ deps = dependsMap lbi+ in setComponentsConfigs lbi+ [ (cn, updateClbi deps comp clbi, cdeps)+ | (cn, clbi, cdeps) <- cc+ , let comp = getComponent pd cn+ ]++ where+ updateClbi deps comp clbi = setUnionDeps (otherDeps deps comp) clbi++ dependsMap ::+ LocalBuildInfo -> [(ComponentName, Deps)]+ dependsMap lbi =+ second getDeps <$> allComponentsInBuildOrder lbi++ otherDeps :: [(ComponentName, Deps)] -> Component -> Deps+ otherDeps deps comp = fromMaybe noDeps $+ flip lookup deps =<< read <$> lookup "x-build-depends-like" fields+ where+ fields = customFieldsBI (componentBuildInfo comp)++-- mostly copypasta from 'defaultInstallHook'+inst ::+ PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()+inst pd lbi _uf ifl = do+ let copyFlags = defaultCopyFlags {+ copyDistPref = installDistPref ifl,+ copyDest = toFlag NoCopyDest,+ copyVerbosity = installVerbosity ifl+ }+ xInstallTarget pd lbi (\pd' lbi' -> install pd' lbi' copyFlags)+ let registerFlags = defaultRegisterFlags {+ regDistPref = installDistPref ifl,+ regInPlace = installInPlace ifl,+ regPackageDB = installPackageDB ifl,+ regVerbosity = installVerbosity ifl+ }+ when (hasLibs pd) $ register pd lbi registerFlags++copy :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO ()+copy pd lbi _uh cf =+ xInstallTarget pd lbi (\pd' lbi' -> install pd' lbi' cf)++xInstallTarget :: PackageDescription+ -> LocalBuildInfo+ -> (PackageDescription -> LocalBuildInfo -> IO ())+ -> IO ()+xInstallTarget pd lbi fn = do+ let (extended, regular) = partition (isJust . installTarget) (executables pd)++ let pd_regular = pd { executables = regular }++ _ <- flip mapM extended $ \exe -> do+ putStrLn $ "extended " ++ show (exeName exe)++ let+ idirtpl = installDirTemplates lbi+ env = installDirsTemplateEnv idirtpl+ libexecdir' = fromPathTemplate (libexecdir idirtpl)++ pd_extended = onlyExePackageDesc [exe] pd+ install_target = fromJust $ installTarget exe+ install_target' = ID.substPathTemplate env install_target+ -- $libexec isn't a real thing :/ so we have to simulate it+ install_target'' = substLibExec' libexecdir' install_target'++ let lbi' = lbi {+ installDirTemplates =+ (installDirTemplates lbi) {+ bindir = install_target''+ }+ }+ fn pd_extended lbi'++ fn pd_regular lbi++ where+ installTarget :: Executable -> Maybe PathTemplate+ installTarget exe =+ toPathTemplate <$> lookup "x-install-target" (customFieldsBI $ buildInfo exe)++ substLibExec libexecdir "$libexecdir" = libexecdir+ substLibExec _ comp = comp++ substLibExec' dir =+ withPT $+ withSP $ map (substLibExec dir . dropTrailingPathSeparator)+++ withPT f pt = toPathTemplate $ f (fromPathTemplate pt)+ withSP f p = joinPath $ f (splitPath p)++onlyExePackageDesc :: [Executable] -> PackageDescription -> PackageDescription+onlyExePackageDesc exes pd = emptyPackageDescription {+ package = package pd+ , executables = exes+ }++parseVer str =+ case filter ((=="") . snd) $ readP_to_S parseVersion str of+ [(ver, _)] -> ver+ _ -> error $ "No parse (Ver) :(\n" ++ str ++ "\n"++-- sanityCheckCabalVersions args cf desc lbi = do+-- (cabalInstallVer, cabalVer) <- getCabalExecVer++-- let+-- ghcVer = compilerVersion (compiler lbi)+-- -- ghc >= 7.10?+-- minGhc710 = ghcVer `withinRange` orLaterVersion (parseVer "7.10")++-- when minGhc710 $ do+-- let cabalHelperCabalVer = compCabalVer (CExeName "cabal-helper")++-- when (not $ cabalVer `sameMajorVersionAs` cabalHelperCabalVer) $+-- failCabalVersionDifferent cabalVer cabalHelperCabalVer++-- -- carry on as usual+-- (postConf simpleUserHooks) args cf desc lbi++-- where+-- earlierVersionThan ver ver' =+-- ver `withinRange` earlierVersion ver'+-- sameMajorVersionAs ver ver' =+-- ver `withinRange` withinVersion (Version (take 2 $ versionBranch ver') [])++-- compCabalVer comp = let+-- clbi = getComponentLocalBuildInfo lbi comp++-- [cabalVer] =+-- [ ver | (_, PackageIdentifier pkg ver) <- componentPackageDeps clbi+-- , pkg == PackageName "Cabal" ]+-- in cabalVer++-- getCabalExecVer = do+-- ["cabal-install", "version", cabalInstallVer, "using", "version", cabalVer, "of", "the", "Cabal", "library"] <- words <$> readProcess "cabal" ["--version"] ""+-- return (parseVer cabalInstallVer, parseVer cabalVer)++-- failCabalVersionDifferent cabalVer libCabalVer =+-- putStrLn rerr >> exitFailure+-- where+-- replace :: String -> String -> String -> String+-- replace _ _ [] = []+-- replace n r h@(h':hs)+-- | map snd (n `zip` h ) == n = r ++ replace n r (drop (length n) h)+-- | otherwise = h':replace n r hs++-- rerr = replace "X.XX.X.X" (showVersion libCabalVer) $+-- replace "Y.YY.Y.Y" (showVersion cabalVer) err+-- err = "\+-- \Error: Cabal seems to have decided ghc-mod should be built using Cabal\n\+-- \X.XX.X.X while the `cabal' executable in your PATH was built with Cabal\n\+-- \Y.YY.Y.Y. This will lead to conflicts when running ghc-mod in any project\n\+-- \where you use this `cabal' executable. Please compile ghc-mod using the same\n\+-- \Cabal version as your `cabal' executable or recompile cabal-install using\n\+-- \this version of the Cabal library.\n\+-- \\n\+-- \See: https://github.com/kazu-yamamoto/ghc-mod/wiki/InconsistentCabalVersions\n"
+ SetupCompat.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE TemplateHaskell, RecordWildCards, StandaloneDeriving #-}+module SetupCompat where++import Control.Arrow+import Control.Monad.Trans.State+import Data.List+import Data.Maybe+import Data.Functor+import Data.Function+import Distribution.Simple.LocalBuildInfo+import Distribution.PackageDescription++import Distribution.Simple+import Distribution.Simple.Setup+import Distribution.Simple.Install++import qualified Data.Map as M+import Data.Map (Map)+++import NotCPP.Declarations+import Language.Haskell.TH++-- $(ifdefD "componentsConfigs" [d| deriving instance (Ord ComponentName) |] )++$(ifD [d|++ showComponentName :: ComponentName -> String+ showComponentName CLibName = "library"+ showComponentName (CExeName name) = "executable '" ++ name ++ "'"+ showComponentName (CTestName name) = "test suite '" ++ name ++ "'"+ showComponentName (CBenchName name) = "benchmark '" ++ name ++ "'"++ |])++$(ifelsedefD "componentsConfigs" [d|++ setComponentsConfigs+ :: LocalBuildInfo+ -> [(ComponentName, ComponentLocalBuildInfo, [ComponentName])]+ -> LocalBuildInfo+ setComponentsConfigs lbi cs = $(recUpdE' (nE "lbi") (mkName "componentsConfigs") (VarE $ mkName "cs"))++ |] [d|++ setComponentsConfigs+ :: LocalBuildInfo+ -> [(ComponentName, ComponentLocalBuildInfo, a)]+ -> LocalBuildInfo+ setComponentsConfigs lbi cs = flip execState lbi $ mapM setClbis gcs+ where+ gcs = groupBy (sameKind `on` fst3) $ sortBy (compare `on` showComponentName . fst3) cs++ fst3 (x,_,_) = x++ sameKind CLibName CLibName = True+ sameKind CLibName _ = False+ sameKind (CExeName _) (CExeName _) = True+ sameKind (CExeName _) _ = False+ sameKind (CTestName _) (CTestName _) = True+ sameKind (CTestName _) _ = False+ sameKind (CBenchName _) (CBenchName _) = True+ sameKind (CBenchName _) _ = False++ setClbis [(CLibName, clbi, _)] =+ get >>= \lbi ->+ put $ $(recUpdE' (nE "lbi") (mkName "libraryConfig") (AppE (ConE (mkName "Just")) (VarE (mkName "clbi"))))++ setClbis cs@((CExeName _, _, _):_) =+ let cfg = (\((CExeName n), clbi, _) -> (n, clbi)) <$> cs in+ get >>= \lbi ->+ put $ $(recUpdE' (nE "lbi") (mkName "executableConfigs") (VarE $ mkName "cfg"))++ setClbis cs@((CTestName _, _, _):_) =+ let cfg = (\((CTestName n), clbi, _) -> (n, clbi)) <$> cs in+ get >>= \lbi ->+ put $ $(recUpdE' (nE "lbi") (mkName "testSuiteConfigs") (VarE $ mkName "cfg"))++ setClbis cs@((CBenchName _, _, _):_) =+ let cfg = (\((CBenchName n), clbi, _) -> (n, clbi)) <$> cs in+ get >>= \lbi ->+ put $ $(recUpdE' (nE "lbi") (mkName "benchmarkConfigs") (VarE $ mkName "cfg"))++ |])+++$(ifD [d|++ componentsConfigs ::+ LocalBuildInfo -> [(ComponentName, ComponentLocalBuildInfo, [ComponentName])]+ componentsConfigs LocalBuildInfo {..} =+ (maybe [] (\c -> [(CLibName, c, [])]) $(nE "libraryConfig"))+ ++ ((\(n, clbi) -> (CExeName n, clbi, [])) <$> $(nE "executableConfigs"))+ ++ ((\(n, clbi) -> (CTestName n, clbi, [])) <$> $(nE "testSuiteConfigs"))+ ++ ((\(n, clbi) -> (CBenchName n, clbi, [])) <$> $(nE "benchmarkConfigs"))++ getComponent :: PackageDescription -> ComponentName -> Component+ getComponent pkg cname =+ case lookupComponent pkg cname of+ Just cpnt -> cpnt+ Nothing -> missingComponent+ where+ missingComponent =+ error $ "internal error: the package description contains no "+ ++ "component corresponding to " ++ show cname++ lookupComponent :: PackageDescription -> ComponentName -> Maybe Component+ lookupComponent pkg CLibName =+ fmap CLib $ library pkg+ lookupComponent pkg (CExeName name) =+ fmap CExe $ find ((name ==) . exeName) (executables pkg)+ lookupComponent pkg (CTestName name) =+ fmap CTest $ find ((name ==) . testName) (testSuites pkg)+ lookupComponent pkg (CBenchName name) =+ fmap CBench $ find ((name ==) . benchmarkName) (benchmarks pkg)++-- We're lying here can't be bothered to order these+ allComponentsInBuildOrder :: LocalBuildInfo+ -> [(ComponentName, ComponentLocalBuildInfo)]+ allComponentsInBuildOrder lbi =+ [ (cname, clbi) | (cname, clbi, _) <- componentsConfigs lbi ]++ getComponentLocalBuildInfo :: LocalBuildInfo -> ComponentName -> ComponentLocalBuildInfo+ getComponentLocalBuildInfo lbi cname =+ case [ clbi+ | (cname', clbi, _) <- componentsConfigs lbi+ , cname == cname' ] of+ [clbi] -> clbi+ _ -> missingComponent+ where+ missingComponent =+ error $ "internal error: there is no configuration data "+ ++ "for component " ++ show cname++ componentBuildInfo :: Component -> BuildInfo+ componentBuildInfo =+ foldComponent libBuildInfo buildInfo testBuildInfo benchmarkBuildInfo++ |])+++$(ifelsedefD "componentPackageRenaming" [d|+ -- M.Map PackageName+ newtype Deps = Deps { unDeps :: ([(InstalledPackageId, PackageId)], Map PackageName $(cT "ModuleRenaming")) }+-- $(return $ TySynD $(mkName "Deps") [] [t| |] )++ noDeps = Deps ([], M.empty)++ getDeps :: ComponentLocalBuildInfo -> Deps+ getDeps = componentPackageDeps &&& $(nE "componentPackageRenaming") >>> Deps++ setUnionDeps :: Deps -> ComponentLocalBuildInfo -> ComponentLocalBuildInfo+ setUnionDeps (Deps (deps, rns)) clbi = let+ clbi' = setComponentPackageRenaming clbi rns+ cpdeps = componentPackageDeps clbi+ in+ clbi' {+ componentPackageDeps = cpdeps `union` deps+ }++ setComponentPackageRenaming clbi cprn =+ -- [| clbi { componentPackageRenaming = componentPackageRenaming clbi `M.union` cprn } |]+ $(recUpdE'+ (nE "clbi")+ (mkName "componentPackageRenaming")+ (InfixE+ (Just+ (AppE+ (VarE+ (mkName "componentPackageRenaming"))+ (VarE (mkName "clbi"))+ ))+ (VarE (mkName "M.union"))+ (Just (VarE (mkName "cprn")))+ )+ )++ |] [d|++ newtype Deps = Deps { unDeps :: [(InstalledPackageId, PackageId)] }++ noDeps = Deps []++ getDeps :: ComponentLocalBuildInfo -> Deps+ getDeps lbi = Deps $ componentPackageDeps lbi++ setUnionDeps :: Deps -> ComponentLocalBuildInfo -> ComponentLocalBuildInfo+ setUnionDeps (Deps deps) clbi = let+ cpdeps = componentPackageDeps clbi+ in+ clbi {+ componentPackageDeps = cpdeps `union` deps+ }+++-- setComponentPackageRenaming clbi _cprn = clbi++ |])
+ Utils.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE CPP #-}+module Utils where++import Control.Applicative+import Data.Traversable+import System.Directory++#if MIN_VERSION_directory(1,2,0)+import Data.Time (UTCTime)+#else+import System.Time (ClockTime)+#endif+import Prelude+++#if MIN_VERSION_directory(1,2,0)+type ModTime = UTCTime+#else+type ModTime = ClockTime+#endif++data TimedFile = TimedFile { tfPath :: FilePath, tfTime :: ModTime }+ deriving (Eq, Show)++instance Ord TimedFile where+ compare (TimedFile _ a) (TimedFile _ b) = compare a b++timeFile :: FilePath -> IO TimedFile+timeFile f = TimedFile <$> pure f <*> getModificationTime f++mightExist :: FilePath -> IO (Maybe FilePath)+mightExist f = do+ exists <- doesFileExist f+ return $ if exists then (Just f) else (Nothing)++timeMaybe :: FilePath -> IO (Maybe TimedFile)+timeMaybe f = traverse timeFile =<< mightExist f
elisp/ghc-check.el view
@@ -20,7 +20,7 @@ :underline (:style wave :color "orangered")) (t :inherit error))- "Face used for marking error lines."+ "Face used for error lines." :group 'ghc) (defface ghc-face-warn@@ -28,7 +28,7 @@ :underline (:style wave :color "gold")) (t :inherit warning))- "Face used for marking warning lines."+ "Face used for warning lines." :group 'ghc) (defface ghc-face-hole@@ -36,7 +36,7 @@ :underline (:style wave :color "purple")) (t :inherit warning))- "Face used for marking hole lines."+ "Face used for hole lines." :group 'ghc) (defvar ghc-check-error-fringe (propertize "!" 'display '(left-fringe exclamation-mark)))@@ -46,27 +46,34 @@ (defvar ghc-check-hole-fringe (propertize "_" 'display '(left-fringe horizontal-bar))) (defvar ghc-display-error nil- "*An action to display errors/warnings for 'M-n' and 'M-p:+ "*How to display errors/warnings when using 'M-n' and 'M-p': -nil does not display errors/warnings.-'minibuffer displays errors/warnings in the minibuffer.-'other-buffer displays errors/warnings in the other buffer.+nil do not display errors/warnings.+'minibuffer display errors/warnings in the minibuffer.+'other-buffer display errors/warnings in a new buffer. ") (defvar ghc-display-hole 'other-buffer- "*An action to display hole information for 'C-c C-j' and 'C-c C-h'+ "*How to display hole information when using 'C-c C-j' and 'C-c C-h' -'minibuffer displays errors/warnings in the minibuffer.-'other-buffer displays errors/warnings in the other buffer"+'minibuffer display errors/warnings in the minibuffer.+'other-buffer display errors/warnings in the a new buffer" ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-check-syntax () (interactive)- (ghc-with-process (ghc-check-send)- 'ghc-check-callback- (lambda () (setq mode-line-process " -:-"))))+ ;; Only check syntax of visible buffers+ (when (and (buffer-file-name)+ (file-exists-p (buffer-file-name))+ (get-buffer-window (current-buffer) t))+ (with-timeout+ (10 (error "ghc process may have hung or exited with an error"))+ (while ghc-process-running (sleep-for 0.1)))+ (ghc-with-process (ghc-check-send)+ 'ghc-check-callback+ (lambda () (setq mode-line-process " -:-"))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -132,7 +139,7 @@ info infos) (dolist (err errs (nreverse infos)) (when (string-match regex err)- (let* ((file (expand-file-name (match-string 1 err))) ;; for Windows+ (let* ((file (expand-file-name (match-string 1 err) ghc-process-root)) ;; for Windows (line (string-to-number (match-string 2 err))) (coln (string-to-number (match-string 3 err))) (msg (match-string 4 err))@@ -167,18 +174,20 @@ ;; If this is a bottleneck for a large code, let's fix. (goto-char (point-min)) (cond- ((and (string= ofile file) hole)- (forward-line (1- line))- (forward-char (1- coln))- (setq beg (point))- (forward-char (length hole))- (setq end (point)))- ((string= ofile file)- (forward-line (1- line))- (while (eq (char-after) 32) (forward-char))- (setq beg (point))- (forward-line)- (setq end (1- (point))))+ ((string= (file-truename ofile) (file-truename file))+ (if hole+ (progn+ (forward-line (1- line))+ (forward-char (1- coln))+ (setq beg (point))+ (forward-char (length hole))+ (setq end (point)))+ (progn+ (forward-line (1- line))+ (forward-char (1- coln))+ (setq beg (point))+ (skip-chars-forward "^[:space:]" (line-end-position))+ (setq end (point))))) (t (setq beg (point)) (forward-line)
elisp/ghc-comp.el view
@@ -127,7 +127,7 @@ (interactive) (if (ghc-should-scroll) (ghc-scroll-completion-buffer)- (ghc-try-complete)))+ (ghc-try-complete))) (defun ghc-should-scroll () (let ((window (ghc-completion-window)))
elisp/ghc-doc.el view
@@ -25,7 +25,7 @@ (setq pkg-ver-path (and mod (ghc-resolve-document-path mod))) (if pkg-ver-path (ghc-display-document pkg-ver-path mod haskell-org expr)- (message "No document found"))))+ (message "No documentation found")))) (ghc-defstruct pkg-ver-path pkg ver path) @@ -93,7 +93,7 @@ (read-from-minibuffer "Module name: " def ghc-input-map)) (defun ghc-read-expression (def)- (read-from-minibuffer "Expression: " def ghc-input-map))+ (read-from-minibuffer "Identifier: " def ghc-input-map)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
elisp/ghc-func.el view
@@ -182,9 +182,19 @@ (funcall ins-func) (goto-char (point-min)) (if (not fontify)- (turn-off-haskell-font-lock)+ ;; turn-off-haskell-font-lock has been removed from haskell-mode+ ;; test if the function is defined in our version+ (if (fboundp 'turn-off-haskell-font-lock)+ (turn-off-haskell-font-lock)+ ;; it's not defined, fallback on font-lock-mode+ (font-lock-mode -1)) (haskell-font-lock-defaults-create)- (turn-on-haskell-font-lock)))+ ;; turn-on-haskell-font-lock has been removed from haskell-mode+ ;; test if the function is defined in our version+ (if (fboundp 'turn-on-haskell-font-lock)+ (turn-on-haskell-font-lock)+ ;; it's not defined, fallback on font-lock-mode+ (turn-on-font-lock)))) (display-buffer buf '((display-buffer-reuse-window display-buffer-pop-up-window))))))
elisp/ghc-info.el view
@@ -82,7 +82,7 @@ (if (null tinfos) (progn (ghc-type-clear-overlay)- (message "Cannot guess type"))+ (message "Cannot determine type")) (let* ((tinfo (nth (ghc-type-get-ix) tinfos)) (type (ghc-tinfo-get-info tinfo)) (beg-line (ghc-tinfo-get-beg-line tinfo))@@ -127,7 +127,7 @@ (defun ghc-expand-th () (interactive) (let* ((file (buffer-file-name))- (cmds (list "expand" file))+ (cmds (list "-b" "\n" "expand" file)) (source (ghc-run-ghc-mod cmds))) (when source (ghc-display
elisp/ghc-pkg.el view
@@ -2,4 +2,4 @@ "ghc" 2.0.0 "Sub mode for Haskell mode"- nil)+ '((haskell-mode "13.0")))
elisp/ghc-process.el view
@@ -10,6 +10,9 @@ (require 'ghc-func) +(defvar ghc-debug-options nil)+;; (setq ghc-debug-options '("-v9"))+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar ghc-process-running nil)@@ -19,37 +22,42 @@ (defvar-local ghc-process-original-file nil) (defvar-local ghc-process-callback nil) (defvar-local ghc-process-hook nil)+(defvar-local ghc-process-root nil) -(defvar ghc-interactive-command "ghc-modi")+(defvar ghc-command "ghc-mod") +(defvar ghc-error-buffer "*GHC Error*")+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-get-project-root () (ghc-run-ghc-mod '("root"))) (defun ghc-with-process (cmd callback &optional hook1 hook2)- (unless ghc-process-process-name- (setq ghc-process-process-name (ghc-get-project-root)))- (when (and ghc-process-process-name (not ghc-process-running))- (setq ghc-process-running t)- (if hook1 (funcall hook1))- (let* ((cbuf (current-buffer))- (name ghc-process-process-name)- (buf (get-buffer-create (concat " ghc-modi:" name)))- (file (buffer-file-name))- (cpro (get-process name)))- (ghc-with-current-buffer buf- (setq ghc-process-original-buffer cbuf)- (setq ghc-process-original-file file)- (setq ghc-process-callback callback)- (setq ghc-process-hook hook2)- (erase-buffer)- (let ((pro (ghc-get-process cpro name buf)))- (process-send-string pro cmd)- (when ghc-debug- (ghc-with-debug-buffer- (insert (format "%% %s" cmd))))- pro)))))+ (let ((root (ghc-get-project-root)))+ (unless ghc-process-process-name+ (setq ghc-process-process-name root))+ (when (and ghc-process-process-name (not ghc-process-running))+ (setq ghc-process-running t)+ (if hook1 (funcall hook1))+ (let* ((cbuf (current-buffer))+ (name ghc-process-process-name)+ (buf (get-buffer-create (concat " ghc-mod:" name)))+ (file (buffer-file-name))+ (cpro (get-process name)))+ (ghc-with-current-buffer buf+ (setq ghc-process-original-buffer cbuf)+ (setq ghc-process-original-file file)+ (setq ghc-process-callback callback)+ (setq ghc-process-hook hook2)+ (setq ghc-process-root root)+ (erase-buffer)+ (let ((pro (ghc-get-process cpro name buf)))+ (process-send-string pro cmd)+ (when ghc-debug+ (ghc-with-debug-buffer+ (insert (format "%% %s" cmd))))+ pro)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -63,37 +71,74 @@ (t cpro))) (defun ghc-start-process (name buf)- (let* ((opts (append '("-b" "\n" "-l") (ghc-make-ghc-options)))- (pro (apply 'start-file-process name buf ghc-interactive-command opts)))+ (let* ((opts (append ghc-debug-options+ '("-b" "\n" "-l" "--line-prefix=O: ,E: ")+ (ghc-make-ghc-options)+ '("legacy-interactive")))+ (pro (apply 'start-file-process name buf ghc-command opts))) (set-process-filter pro 'ghc-process-filter) (set-process-sentinel pro 'ghc-process-sentinel) (set-process-query-on-exit-flag pro nil) pro)) (defun ghc-process-filter (process string)- (let ((pbuf (process-buffer process)))+ (let* ((pbuf (process-buffer process))+ (tbufname (concat " tmp " (buffer-name pbuf)))+ tbuf) (if (not (get-buffer pbuf)) (setq ghc-process-running nil) ;; just in case- (ghc-with-current-buffer (process-buffer process)- (goto-char (point-max))- (insert string)+ (ghc-with-current-buffer pbuf+ (when ghc-debug+ (ghc-with-debug-buffer+ (insert string)))+ (with-current-buffer (get-buffer-create tbufname)+ (setq tbuf (current-buffer))+ (goto-char (point-max))+ (insert string)+ (goto-char (point-min))+ (let ((cont t) end out)+ (while (and cont (not (eobp)))+ (cond+ ((looking-at "^O: ")+ (setq out t))+ ((looking-at "^E: ")+ (setq out nil))+ (t+ (setq cont nil)))+ (when cont+ (forward-line)+ (unless (bolp) (setq cont nil)))+ (when cont+ (delete-region 1 4)+ (setq end (point))+ (if out+ (with-current-buffer pbuf+ (goto-char (point-max))+ (insert-buffer-substring tbuf 1 end))+ (with-current-buffer (get-buffer-create ghc-error-buffer)+ (setq buffer-read-only t)+ (let* ((buffer-read-only nil)+ (inhibit-read-only t)+ (cbuf (current-buffer))+ cwin)+ (unless (get-buffer-window cbuf) (display-buffer cbuf))+ (setq cwin (get-buffer-window cbuf))+ (with-selected-window cwin+ (goto-char (point-max))+ (insert-buffer-substring tbuf 1 end)+ (set-buffer-modified-p nil)+ (redisplay)))))+ (delete-region 1 end)))))+ (goto-char (point-max)) (forward-line -1) (cond ((looking-at "^OK$") (if ghc-process-hook (funcall ghc-process-hook)) (goto-char (point-min)) (funcall ghc-process-callback 'ok)- (when ghc-debug- (let ((cbuf (current-buffer)))- (ghc-with-debug-buffer- (insert-buffer-substring cbuf)))) (setq ghc-process-running nil)) ((looking-at "^NG ") (funcall ghc-process-callback 'ng)- (when ghc-debug- (let ((cbuf (current-buffer)))- (ghc-with-debug-buffer- (insert-buffer-substring cbuf)))) (setq ghc-process-running nil))))))) (defun ghc-process-sentinel (process event)
elisp/ghc.el view
@@ -28,7 +28,7 @@ (< emacs-minor-version minor))) (error "ghc-mod requires at least Emacs %d.%d" major minor))) -(defconst ghc-version "5.2.1.2")+(defconst ghc-version "5.3.0.0") ;; (eval-when-compile ;; (require 'haskell-mode))@@ -117,6 +117,9 @@ (setq ghc-initialized t) (defadvice save-buffer (after ghc-check-syntax-on-save activate) "Check syntax with GHC when a haskell-mode buffer is saved."+ (when (eq 'haskell-mode major-mode) (ghc-check-syntax)))+ (defadvice switch-to-buffer (after ghc-check-syntax-on-switch-to-buffer activate)+ "Check syntax with GHC when switching to a haskell-mode buffer." (when (eq 'haskell-mode major-mode) (ghc-check-syntax)))) (ghc-import-module) (ghc-check-syntax))@@ -130,23 +133,19 @@ (let ((el-path (locate-file "ghc.el" load-path)) (ghc-path (executable-find "ghc")) ;; FIXME (ghc-mod-path (executable-find ghc-module-command))- (ghc-modi-path (executable-find ghc-interactive-command)) (el-ver ghc-version) (ghc-ver (ghc-run-ghc-mod '("--version") "ghc")) (ghc-mod-ver (ghc-run-ghc-mod '("version")))- (ghc-modi-ver (ghc-run-ghc-mod '("version") ghc-interactive-command)) (path (getenv "PATH"))) (switch-to-buffer (get-buffer-create "**GHC Debug**")) (erase-buffer) (insert "Path: check if you are using intended programs.\n") (insert (format "\t ghc.el path: %s\n" el-path)) (insert (format "\t ghc-mod path: %s\n" ghc-mod-path))- (insert (format "\tghc-modi path: %s\n" ghc-modi-path)) (insert (format "\t ghc path: %s\n" ghc-path))- (insert "\nVersion: all versions must be the same.\n")+ (insert "\nVersion: all GHC versions must be the same.\n") (insert (format "\t ghc.el version %s\n" el-ver)) (insert (format "\t %s\n" ghc-mod-ver))- (insert (format "\t%s\n" ghc-modi-ver)) (insert (format "\t%s\n" ghc-ver)) (insert "\nEnvironment variables:\n") (insert (format "\tPATH=%s\n" path))))
ghc-mod.cabal view
@@ -1,73 +1,102 @@ Name: ghc-mod-Version: 5.2.1.2-Author: Kazu Yamamoto <kazu@iij.ad.jp>- Daniel Gröber <dxld@darkboxed.org>+Version: 5.3.0.0+Author: Kazu Yamamoto <kazu@iij.ad.jp>,+ Daniel Gröber <dxld@darkboxed.org>, Alejandro Serrano <trupill@gmail.com>-Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>-License: BSD3+Maintainer: Daniel Gröber <dxld@darkboxed.org>+License: AGPL-3 License-File: LICENSE+License-Files: COPYING.BSD3 COPYING.AGPL3 Homepage: http://www.mew.org/~kazu/proj/ghc-mod/ Synopsis: Happy Haskell Programming-Description: The ghc-mod command is a backend command to enrich- Haskell programming on editors including- Emacs, Vim, and Sublime.- The ghc-mod command is based on ghc-mod library- which is a wrapper of GHC API.- This package includes the ghc-mod command,- the ghc-mod library, and Emacs front-end- (for historical reasons).- For more information, please see its home page.+Description:+ ghc-mod is a backend program to enrich Haskell programming in editors. It+ strives to offer most of the features one has come to expect from modern IDEs+ in any editor. -Category: Development-Cabal-Version: >= 1.10-Build-Type: Simple-Data-Dir: elisp-Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el- ghc-check.el ghc-process.el ghc-command.el ghc-info.el- ghc-ins-mod.el ghc-indent.el ghc-pkg.el ghc-rewrite.el+ ghc-mod provides a library for other haskell programs to use as well as a+ standalone program for easy editor integration. All of the fundamental+ functionality of the frontend program can be accessed through the library+ however many implementation details are hidden and if you want to+ significantly extend ghc-mod you should submit these changes upstream instead+ of implementing them on top of the library.++ For more information, please see its home page.++Category: GHC, Development+Cabal-Version: >= 1.14+Build-Type: Custom+Data-Files: elisp/Makefile+ elisp/*.el+Data-Files: LICENSE COPYING.BSD3 COPYING.AGPL3 Extra-Source-Files: ChangeLog- test/data/*.cabal- test/data/*.hs- test/data/cabal.sandbox.config.in- test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf+ SetupCompat.hs+ NotCPP/*.hs+ test/data/annotations/*.hs test/data/broken-cabal/*.cabal test/data/broken-cabal/cabal.sandbox.config.in- test/data/broken-sandbox/*.cabal test/data/broken-sandbox/cabal.sandbox.config+ test/data/broken-sandbox/dummy.cabal+ test/data/cabal-flags/cabal-flags.cabal+ test/data/cabal-project/*.cabal+ test/data/cabal-project/*.hs+ test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf+ test/data/cabal-project/cabal.sandbox.config.in+ test/data/cabal-project/subdir1/subdir2/dummy test/data/case-split/*.hs- test/data/cabal-flags/*.cabal+ test/data/check-packageid/cabal.sandbox.config.in+ test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf test/data/check-test-subdir/*.cabal test/data/check-test-subdir/src/Check/Test/*.hs test/data/check-test-subdir/test/*.hs test/data/check-test-subdir/test/Bar/*.hs- test/data/check-packageid/cabal.sandbox.config.in- test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf test/data/duplicate-pkgver/cabal.sandbox.config.in test/data/duplicate-pkgver/duplicate-pkgver.cabal test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-1.0-7c59d13f32294d1ef6dc6233c24df961.conf test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-14e543bdae2da4d2aeff5386892c9112.conf test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/template-haskell-2.8.0.0-32d4f24abdbb6bf41272b183b2e23e9c.conf- test/data/pattern-synonyms/*.cabal- test/data/pattern-synonyms/*.hs+ test/data/foreign-export/*.hs test/data/ghc-mod-check/*.cabal test/data/ghc-mod-check/*.hs- test/data/ghc-mod-check/Data/*.hs- test/data/subdir1/subdir2/dummy- test/data/.cabal-sandbox/packages/00-index.tar+ test/data/ghc-mod-check/lib/Data/*.hs+ test/data/hlint/*.hs+ test/data/home-module-graph/cpp/*.hs+ test/data/home-module-graph/cycle/*.hs+ test/data/home-module-graph/errors/*.hs+ test/data/home-module-graph/indirect/*.hs+ test/data/home-module-graph/indirect-update/*.hs+ test/data/import-cycle/*.hs+ test/data/non-exported/*.hs+ test/data/pattern-synonyms/*.cabal+ test/data/pattern-synonyms/*.hs+ test/data/quasi-quotes/*.hs+ test/data/template-haskell/*.hs+ test/data/target/*.hs+ test/data/check-missing-warnings/*.hs+ test/data/custom-cradle/custom-cradle.cabal+ test/data/custom-cradle/ghc-mod.package-db-stack+ test/data/custom-cradle/package-db-a/.gitkeep+ test/data/custom-cradle/package-db-b/.gitkeep+ test/data/custom-cradle/package-db-c/.gitkeep+ test/data/cabal-preprocessors/*.cabal+ test/data/cabal-preprocessors/*.hs+ test/data/cabal-preprocessors/*.hsc Library Default-Language: Haskell2010- GHC-Options: -Wall- Default-Extensions: ConstraintKinds, FlexibleContexts+ GHC-Options: -Wall -fno-warn-deprecations+ Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns,+ ConstraintKinds, FlexibleContexts,+ DataKinds, KindSignatures, TypeOperators Exposed-Modules: Language.Haskell.GhcMod Language.Haskell.GhcMod.Internal- Other-Modules: Language.Haskell.GhcMod.Boot+ Other-Modules: Paths_ghc_mod+ Utils+ Language.Haskell.GhcMod.Boot Language.Haskell.GhcMod.Browse- Language.Haskell.GhcMod.Cabal16- Language.Haskell.GhcMod.Cabal18- Language.Haskell.GhcMod.Cabal21- Language.Haskell.GhcMod.CabalApi- Language.Haskell.GhcMod.CabalConfig+ Language.Haskell.GhcMod.CabalHelper+ Language.Haskell.GhcMod.Caching+ Language.Haskell.GhcMod.Caching.Types Language.Haskell.GhcMod.CaseSplit Language.Haskell.GhcMod.Check Language.Haskell.GhcMod.Convert@@ -79,18 +108,21 @@ Language.Haskell.GhcMod.FillSig Language.Haskell.GhcMod.Find Language.Haskell.GhcMod.Flag- Language.Haskell.GhcMod.GHCApi- Language.Haskell.GhcMod.GHCChoice Language.Haskell.GhcMod.Gap Language.Haskell.GhcMod.GhcPkg+ Language.Haskell.GhcMod.HomeModuleGraph Language.Haskell.GhcMod.Info Language.Haskell.GhcMod.Lang Language.Haskell.GhcMod.Lint Language.Haskell.GhcMod.Logger+ Language.Haskell.GhcMod.Logging Language.Haskell.GhcMod.Modules Language.Haskell.GhcMod.Monad+ Language.Haskell.GhcMod.Monad.Types+ Language.Haskell.GhcMod.Output Language.Haskell.GhcMod.PathsAndFiles Language.Haskell.GhcMod.PkgDoc+ Language.Haskell.GhcMod.Pretty Language.Haskell.GhcMod.Read Language.Haskell.GhcMod.SrcUtils Language.Haskell.GhcMod.Target@@ -98,7 +130,10 @@ Language.Haskell.GhcMod.Utils Language.Haskell.GhcMod.World Build-Depends: base >= 4.0 && < 5+ , bytestring+ , cereal >= 0.4 , containers+ , cabal-helper == 0.5.* && >= 0.5.1.0 , deepseq , directory , filepath@@ -106,7 +141,6 @@ , ghc-paths , ghc-syb-utils , hlint >= 1.8.61- , io-choice , monad-journal >= 0.4 , old-time , pretty@@ -117,30 +151,28 @@ , transformers , transformers-base , mtl >= 2.0- , monad-control+ , monad-control >= 1 , split , haskell-src-exts , text , djinn-ghc >= 0.0.2.2- if impl(ghc >= 7.8)- Build-Depends: Cabal >= 1.18- else+ , fclabels+ if impl(ghc < 7.8) Build-Depends: convertible- , Cabal >= 1.10 && < 1.17- if impl(ghc <= 7.4.2)+ if impl(ghc < 7.5) -- Only used to constrain random to a version that still works with GHC 7.4- Build-Depends: random <= 1.0.1.1+ Build-Depends: random <= 1.0.1.1,+ ghc-prim Executable ghc-mod Default-Language: Haskell2010 Main-Is: GHCMod.hs Other-Modules: Paths_ghc_mod- GHC-Options: -Wall+ GHC-Options: -Wall -fno-warn-deprecations Default-Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 , async- , data-default , directory , filepath , pretty@@ -156,22 +188,17 @@ Other-Modules: Paths_ghc_mod Misc Utils- GHC-Options: -Wall -threaded+ GHC-Options: -Wall -threaded -fno-warn-deprecations if os(windows) Cpp-Options: -DWINDOWS Default-Extensions: ConstraintKinds, FlexibleContexts- HS-Source-Dirs: src+ HS-Source-Dirs: src, . Build-Depends: base >= 4.0 && < 5- , async- , containers , directory , filepath- , old-time , process- , split , time- , ghc- , ghc-mod+ , old-time Test-Suite doctest Type: exitcode-stdio-1.0@@ -180,20 +207,27 @@ Ghc-Options: -Wall Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs+ if impl(ghc == 7.4.*)+ Buildable: False Build-Depends: base , doctest >= 0.9.3 Test-Suite spec Default-Language: Haskell2010- Default-Extensions: ConstraintKinds, FlexibleContexts+ Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns,+ ConstraintKinds, FlexibleContexts,+ DataKinds, KindSignatures, TypeOperators Main-Is: Main.hs Hs-Source-Dirs: test, .- Ghc-Options: -Wall+ Ghc-Options: -Wall -fno-warn-deprecations+ CPP-Options: -DSPEC=1 Type: exitcode-stdio-1.0- Other-Modules: BrowseSpec- CabalApiSpec- CheckSpec+ Other-Modules: Paths_ghc_mod Dir+ Spec+ TestUtils+ BrowseSpec+ CheckSpec FlagSpec InfoSpec LangSpec@@ -201,42 +235,14 @@ ListSpec MonadSpec PathsAndFilesSpec- Spec- TestUtils- Build-Depends: base >= 4.0 && < 5- , containers- , deepseq- , directory- , filepath- , ghc- , ghc-paths- , ghc-syb-utils- , hlint >= 1.7.1- , io-choice- , monad-journal >= 0.4- , old-time- , pretty- , process- , syb- , temporary- , time- , transformers- , transformers-base- , mtl >= 2.0- , monad-control- , hspec >= 1.8.2- , split- , haskell-src-exts- , text- , djinn-ghc >= 0.0.2.2- if impl(ghc >= 7.8)- Build-Depends: Cabal >= 1.18- else- Build-Depends: convertible- , Cabal >= 1.10 && < 1.17- if impl(ghc < 7.6)- Build-Depends: executable-path- CPP-Options: -DSPEC=1+ HomeModuleGraphSpec++ Build-Depends: hspec >= 2.0.0+ if impl(ghc == 7.4.*)+ Build-Depends: executable-path+ X-Build-Depends-Like: CLibName++ Source-Repository head Type: git
src/GHCMod.hs view
@@ -6,41 +6,40 @@ import MonadUtils (liftIO) import Control.Applicative import Control.Monad-import Control.Exception ( SomeException(..), fromException, Exception- , Handler(..), catches, throw) import Data.Typeable (Typeable) import Data.Version (showVersion)-import Data.Default import Data.List import Data.List.Split-import Data.Maybe import Data.Char (isSpace)+import Data.Maybe+import Exception import Language.Haskell.GhcMod-import Language.Haskell.GhcMod.Internal+import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO) import Paths_ghc_mod import System.Console.GetOpt (OptDescr(..), ArgDescr(..), ArgOrder(..)) import qualified System.Console.GetOpt as O-import System.Directory (setCurrentDirectory)-import System.Environment (getArgs,getProgName)+import System.FilePath ((</>))+import System.Directory (setCurrentDirectory, getAppUserDataDirectory,+ removeDirectoryRecursive)+import System.Environment (getArgs) import System.Exit (exitFailure)-import System.IO (hPutStrLn, stdout, stderr, hSetEncoding, utf8, hFlush)-import System.IO.Unsafe (unsafePerformIO)-import System.FilePath (takeFileName)-import System.Exit (ExitCode, exitSuccess)+import System.IO (stdout, hSetEncoding, utf8, hFlush)+import System.Exit (exitSuccess) import Text.PrettyPrint+import Prelude import Misc -+progVersion :: String -> String+progVersion pf =+ "ghc-mod"++pf++" version " ++ showVersion version ++ " compiled by GHC "+ ++ cProjectVersion ++ "\n" -progVersion :: String-progVersion =- progName ++ " version " ++ showVersion version ++ " compiled by GHC "- ++ cProjectVersion ++ "\n"+ghcModVersion :: String+ghcModVersion = progVersion "" --- TODO: remove (ghc) version prefix!-progName :: String-progName = unsafePerformIO $ takeFileName <$> getProgName+ghcModiVersion :: String+ghcModiVersion = progVersion "i" optionUsage :: (String -> String) -> [OptDescr a] -> [String] optionUsage indent opts = concatMap optUsage opts@@ -65,33 +64,27 @@ ReqArg _ label -> s ++ label OptArg _ label -> s ++ "["++label++"]" +-- TODO: Generate the stuff below automatically usage :: String usage =- case progName of- "ghc-modi" -> ghcModiUsage- _ -> ghcModUsage---- TODO: Generate the stuff below automatically-ghcModUsage :: String-ghcModUsage =- "Usage: ghc-mod [OPTIONS...] COMMAND [OPTIONS...] \n\+ "Usage: ghc-mod [OPTIONS...] COMMAND [CMD_ARGS...] \n\ \*Global Options (OPTIONS)*\n\ \ Global options can be specified before and after the command and\n\ \ interspersed with command specific options\n\ \\n" ++ (unlines $ indent <$> optionUsage indent globalArgSpec) ++ "*Commands*\n\- \ - version | --version\n\+ \ - version\n\ \ Print the version of the program.\n\ \\n\- \ - help | --help\n\+ \ - help\n\ \ Print this help message.\n\ \\n\ \ - list [FLAGS...] | modules [FLAGS...]\n\ \ List all visible modules.\n\ \ Flags:\n\ \ -d\n\- \ Also print the modules' package.\n\+ \ Print package modules belong to.\n\ \\n\ \ - lang\n\ \ List all known GHC language extensions.\n\@@ -183,12 +176,12 @@ \ -l\n\ \ Option to be passed to hlint.\n\ \\n\- \ - root FILE\n\- \ Try to find the project directory given FILE. For Cabal\n\- \ projects this is the directory containing the cabal file, for\n\- \ projects that use a cabal sandbox but have no cabal file this is the\n\- \ directory containing the sandbox and otherwise this is the directory\n\- \ containing FILE.\n\+ \ - root\n\+ \ Try to find the project directory. For Cabal projects this is the\n\+ \ directory containing the cabal file, for projects that use a cabal\n\+ \ sandbox but have no cabal file this is the directory containing the\n\+ \ cabal.sandbox.config file and otherwise this is the current\n\+ \ directory.\n\ \\n\ \ - doc MODULE\n\ \ Try finding the html documentation directory for the given MODULE.\n\@@ -197,57 +190,46 @@ \ Print debugging information. Please include the output in any bug\n\ \ reports you submit.\n\ \\n\+ \ - debugComponent [MODULE_OR_FILE...]\n\+ \ Debugging information related to cabal component resolution.\n\+ \\n\ \ - boot\n\- \ Internal command used by the emacs frontend.\n"- -- "\n\- -- \The following forms are supported so ghc-mod can be invoked by\n\- -- \`cabal repl':\n\- -- \\n\- -- \ ghc-mod --make GHC_OPTIONS\n\- -- \ Pass all options through to the GHC executable.\n\- -- \\n\- -- \ ghc-mod --interactive GHC_OPTIONS [--ghc-mod]\n\- -- \ Start ghci emulation mode. GHC_OPTIONS are passed to the\n\- -- \ GHC API. If `--ghc-mod' is given ghc-mod specific extensions\n\- -- \ are enabled.\n"- where- indent = (" "++)--ghcModiUsage :: String-ghcModiUsage =- "Usage: ghc-modi [OPTIONS...] COMMAND\n\- \*Options*\n"- ++ (unlines $ indent <$> optionUsage indent globalArgSpec) ++- "*Commands*\n\- \ - version | --version\n\- \ Print the version of the program.\n\+ \ Internal command used by the emacs frontend.\n\ \\n\- \ - help | --help\n\- \ Print this help message.\n"+ \ - legacy-interactive\n\+ \ ghc-modi compatibility mode.\n" where indent = (" "++) --- cmdUsage :: String -> String -> String-cmdUsage cmd s =+cmdUsage cmd realUsage = let -- Find command head- a = dropWhile (not . ((" - " ++ cmd) `isInfixOf`)) $ lines s+ a = dropWhile (not . isCmdHead) $ lines realUsage -- Take til the end of the current command block b = flip takeWhile a $ \l ->- all isSpace l || (isIndented l && (isCurrCmdHead l || isNotCmdHead l))+ all isSpace l || (isIndented l && (isCmdHead l || isNotCmdHead l)) -- Drop extra newline from the end c = dropWhileEnd (all isSpace) b isIndented = (" " `isPrefixOf`) isNotCmdHead = ( not . (" - " `isPrefixOf`))- isCurrCmdHead = ((" - " ++ cmd) `isPrefixOf`) + containsAnyCmdHead s = ((" - ") `isInfixOf` s)+ containsCurrCmdHead s = ((" - " ++ cmd) `isInfixOf` s)+ isCmdHead s =+ containsAnyCmdHead s &&+ or [ containsCurrCmdHead s+ , any (cmd `isPrefixOf`) (splitOn " | " s)+ ]+ unindent (' ':' ':' ':' ':l) = l unindent l = l in unlines $ unindent <$> c++ghcModStyle :: Style+ghcModStyle = style { lineLength = 80, ribbonsPerLine = 1.2 }+ ---------------------------------------------------------------- option :: [Char] -> [String] -> String -> ArgDescr a -> OptDescr a@@ -256,43 +238,78 @@ reqArg :: String -> (String -> a) -> ArgDescr a reqArg udsc dsc = ReqArg dsc udsc -globalArgSpec :: [OptDescr (Options -> Options)]+optArg :: String -> (Maybe String -> a) -> ArgDescr a+optArg udsc dsc = OptArg dsc udsc++intToLogLevel :: Int -> GmLogLevel+intToLogLevel = toEnum++globalArgSpec :: [OptDescr (Options -> Either [String] Options)] globalArgSpec =- [ option "v" ["verbose"] "Be more verbose." $- NoArg $ \o -> o { ghcUserOptions = "-v" : ghcUserOptions o }+ [ option "v" ["verbose"] "Increase or set log level. (0-7)" $+ optArg "LEVEL" $ \ml o -> Right $ o {+ logLevel = case ml of+ Nothing -> increaseLogLevel (logLevel o)+ Just l -> toEnum $ min 7 $ read l+ } + , option "s" [] "Be silent, set log level to 0" $+ NoArg $ \o -> Right $ o { logLevel = toEnum 0 }+ , option "l" ["tolisp"] "Format output as an S-Expression" $- NoArg $ \o -> o { outputStyle = LispStyle }+ NoArg $ \o -> Right $ o { outputStyle = LispStyle } - , option "b" ["boundary"] "Output line separator"$- reqArg "SEP" $ \s o -> o { lineSeparator = LineSeparator s }+ , option "b" ["boundary", "line-seperator"] "Output line separator"$+ reqArg "SEP" $ \s o -> Right $ o { lineSeparator = LineSeparator s }+ , option "" ["line-prefix"] "Output line separator"$+ reqArg "OUT,ERR" $ \s o -> let+ [out, err] = splitOn "," s+ in Right $ o { linePrefix = Just (out, err) } - , option "g" ["ghcOpt"] "Option to be passed to GHC" $- reqArg "OPT" $ \g o ->+ , option "g" ["ghcOpt", "ghc-option"] "Option to be passed to GHC" $+ reqArg "OPT" $ \g o -> Right $ o { ghcUserOptions = g : ghcUserOptions o } , option "" ["with-ghc"] "GHC executable to use" $- reqArg "PROG" $ \p o -> o { ghcProgram = p }+ reqArg "PROG" $ \p o -> Right $ o { ghcProgram = p } + , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $+ reqArg "PROG" $ \p o -> Right $ o { ghcPkgProgram = p }+ , option "" ["with-cabal"] "cabal-install executable to use" $- reqArg "PROG" $ \p o -> o { cabalProgram = p }+ reqArg "PROG" $ \p o -> Right $ o { cabalProgram = p }++ , option "" ["version"] "print version information" $+ NoArg $ \_ -> Left ["version"]++ , option "" ["help"] "print this help message" $+ NoArg $ \_ -> Left ["help"]+ ] parseGlobalArgs :: [String] -> Either InvalidCommandLine (Options, [String]) parseGlobalArgs argv- = case O.getOpt RequireOrder globalArgSpec argv of- (o,r,[] ) -> Right $ (foldr id defaultOptions o, r)- (_,_,errs) -> Left $ InvalidCommandLine $ Right $- "Parsing command line options failed: " ++ concat errs+ = case O.getOpt' RequireOrder globalArgSpec argv of+ (o,r,u,[]) -> case foldr (=<<) (Right defaultOptions) o of+ Right o' -> Right (o', u ++ r)+ Left c -> Right (defaultOptions, c)+ (_,_,u,e) -> Left $ InvalidCommandLine $ Right $+ "Parsing command line options failed: "+ ++ concat (e ++ map errUnrec u)+ where+ errUnrec :: String -> String+ errUnrec optStr = "unrecognized option `" ++ optStr ++ "'\n" -parseCommandArgs :: [OptDescr (Options -> Options)]+parseCommandArgs :: [OptDescr (Options -> Either [String] Options)] -> [String] -> Options -> (Options, [String]) parseCommandArgs spec argv opts = case O.getOpt RequireOrder (globalArgSpec ++ spec) argv of- (o,r,[]) -> (foldr id opts o, r)+ (o,r,[]) -> case foldr (=<<) (Right opts) o of+ Right o' -> (o', r)+ Left c -> (defaultOptions, c) (_,_,errs) -> fatalError $ "Parsing command options failed: " ++ concat errs @@ -306,121 +323,65 @@ instance Exception CmdError ------------------------------------------------------------------ data InteractiveOptions = InteractiveOptions { ghcModExtensions :: Bool } -instance Default InteractiveOptions where- def = InteractiveOptions False--handler :: IO a -> IO a-handler = flip catches $- [ Handler $ \(FatalError msg) -> exitError msg- , Handler $ \(InvalidCommandLine e) -> do+handler :: IOish m => GhcModT m a -> GhcModT m a+handler = flip gcatches $+ [ GHandler $ \(FatalError msg) -> exitError msg+ , GHandler $ \(InvalidCommandLine e) -> do case e of Left cmd ->- exitError $ (cmdUsage cmd ghcModUsage) ++ "\n"- ++ progName ++ ": Invalid command line form."- Right msg -> exitError $ progName ++ ": " ++ msg+ exitError $ "Usage for `"++cmd++"' command:\n\n"+ ++ (cmdUsage cmd usage) ++ "\n"+ ++ "ghc-mod: Invalid command line form."+ Right msg -> exitError $ "ghc-mod: " ++ msg+ , GHandler $ \(SomeException e) -> exitError $ "ghc-mod: " ++ show e ] main :: IO ()-main = handler $ do+main = do hSetEncoding stdout utf8 args <- getArgs-- -- This doesn't handle --help and --version being given after any global- -- options. To do that we'd have to fiddle with getOpt. case parseGlobalArgs args of- Left e -> case globalCommands args of- Just s -> putStr s- Nothing -> throw e-- Right res@(_,cmdArgs) ->- case globalCommands cmdArgs of- Just s -> putStr s- Nothing -> progMain res+ Left e -> throw e+ Right res -> progMain res progMain :: (Options,[String]) -> IO ()-progMain (globalOptions,cmdArgs) = do- -- let (ghcArgs, modArgs) = second stripSeperator $ span (/="--") args- -- _realGhcArgs = filter (/="--ghc-mod") ghcArgs-- -- (globalOptions,_cmdArgs) = parseGlobalArgs modArgs-- -- stripSeperator ("--":rest) = rest- -- stripSeperator l = l-- case progName of- "ghc-modi" -> do- legacyInteractive globalOptions =<< emptyNewUnGetLine--- _- -- | "--numeric-version" `elem` ghcArgs || "--make" `elem` ghcArgs -> do- -- rawSystem (ghcProgram globalOptions) realGhcArgs >>= exitWith-- -- | "--interactive" `elem` ghcArgs -> do- -- let interactiveOptions = if "--ghc-mod" `elem` ghcArgs- -- then def { ghcModExtensions = True }- -- else def-- -- -- TODO: pass ghcArgs' to ghc API- -- putStrLn "\ninteractive\n"- -- --print realGhcArgs- -- (res, _) <- runGhcModT globalOptions $ undefined- -- case res of- -- Right s -> putStr s- -- Left e -> exitError $ render (gmeDoc e)--- | otherwise -> do- (res,_) <- runGhcModT globalOptions $ ghcCommands cmdArgs- case res of- Right s -> putStr s- Left e -> exitError $ render (gmeDoc e)-- -- Obtain ghc options by letting ourselfs be executed by- -- @cabal repl@- -- TODO: need to do something about non-cabal projects- -- exe <- ghcModExecutable- -- let cabalArgs = ["repl", "-v0", "--with-ghc="++exe]- -- ++ (("--ghc-option="++) `map` ("--ghc-mod":"--":args))-- -- print cabalArgs-- -- rawSystem "cabal" cabalArgs >>= exitWith------ ghc-modi-legacyInteractive :: Options -> UnGetLine -> IO ()-legacyInteractive opt ref = flip catches handlers $ do- (res,_) <- runGhcModT opt $ do- symdbreq <- liftIO $ newSymDbReq opt- world <- liftIO . getCurrentWorld =<< cradle- legacyInteractiveLoop symdbreq ref world-- case res of- Right () -> return ()- Left e -> putStrLn $ notGood $ render (gmeDoc e)-+progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do+ case globalCommands cmdArgs of+ Just s -> gmPutStr s+ Nothing -> ghcCommands cmdArgs where- handlers = [ Handler $ \Restart -> legacyInteractive opt ref ]+ hndle action = do+ (e, _l) <- action+ case e of+ Right _ ->+ return ()+ Left ed ->+ exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc ed) -isExitCodeException :: SomeException -> Bool-isExitCodeException e = isJust mExitCode- where- mExitCode :: Maybe ExitCode- mExitCode = fromException e+globalCommands :: [String] -> Maybe String+globalCommands (cmd:_)+ | cmd == "help" = Just usage+ | cmd == "version" = Just ghcModVersion+globalCommands _ = Nothing +-- ghc-modi+legacyInteractive :: IOish m => GhcModT m ()+legacyInteractive = do+ opt <- options+ prepareCabalHelper+ tmpdir <- cradleTempDir <$> cradle+ symdbreq <- liftIO $ newSymDbReq opt tmpdir+ world <- getCurrentWorld+ legacyInteractiveLoop symdbreq world -bug :: String -> IO ()+bug :: IOish m => String -> GhcModT m () bug msg = do- putStrLn $ notGood $ "BUG: " ++ msg- exitFailure+ gmPutStrLn $ notGood $ "BUG: " ++ msg+ liftIO exitFailure notGood :: String -> String notGood msg = "NG " ++ escapeNewlines msg@@ -431,30 +392,26 @@ replace :: String -> String -> String -> String replace needle replacement = intercalate replacement . splitOn needle - legacyInteractiveLoop :: IOish m- => SymDbReq -> UnGetLine -> World -> GhcModT m ()-legacyInteractiveLoop symdbreq ref world = do+ => SymDbReq -> World -> GhcModT m ()+legacyInteractiveLoop symdbreq world = do liftIO . setCurrentDirectory =<< cradleRootDir <$> cradle -- blocking- cmdArg <- liftIO $ getCommand ref+ cmdArg <- liftIO $ getLine -- after blocking, we need to see if the world has changed. - changed <- liftIO . didWorldChange world =<< cradle+ changed <- didWorldChange world when changed $ do- liftIO $ ungetCommand ref cmdArg- throw Restart-- liftIO . prepareAutogen =<< cradle+ dropSession let (cmd':args') = split (keepDelimsR $ condense $ whenElt isSpace) cmdArg arg = concat args' cmd = dropWhileEnd isSpace cmd' args = dropWhileEnd isSpace `map` args' - res <- case dropWhileEnd isSpace cmd of+ res <- flip gcatches interactiveHandlers $ case dropWhileEnd isSpace cmd of "check" -> checkSyntaxCmd [arg] "lint" -> lintCmd [arg] "find" -> do@@ -476,22 +433,20 @@ "" -> liftIO $ exitSuccess _ -> fatalError $ "unknown command: `" ++ cmd ++ "'" - liftIO $ putStr res >> putStrLn "OK" >> hFlush stdout- legacyInteractiveLoop symdbreq ref world---globalCommands :: [String] -> Maybe String-globalCommands [] = Nothing-globalCommands (cmd:_) = case cmd of- _ | cmd == "help" || cmd == "--help" -> Just usage- _ | cmd == "version" || cmd == "--version" -> Just progVersion- _ -> Nothing+ gmPutStr res >> gmPutStrLn "OK" >> liftIO (hFlush stdout)+ legacyInteractiveLoop symdbreq world+ where+ interactiveHandlers =+ [ GHandler $ \e@(FatalError _) -> throw e+ , GHandler $ \(SomeException e) -> gmErrStrLn (show e) >> return ""+ ] -ghcCommands :: IOish m => [String] -> GhcModT m String+ghcCommands :: IOish m => [String] -> GhcModT m () ghcCommands [] = fatalError "No command given (try --help)"-ghcCommands (cmd:args) = fn args+ghcCommands (cmd:args) = do+ gmPutStr =<< action args where- fn = case cmd of+ action = case cmd of _ | cmd == "list" || cmd == "modules" -> modulesCmd "lang" -> languagesCmd "flag" -> flagsCmd@@ -499,6 +454,7 @@ "check" -> checkSyntaxCmd "expand" -> expandTemplateCmd "debug" -> debugInfoCmd+ "debug-component" -> componentInfoCmd "info" -> infoCmd "type" -> typesCmd "split" -> splitsCmd@@ -511,6 +467,8 @@ "doc" -> pkgDocCmd "dumpsym" -> dumpSymbolCmd "boot" -> bootCmd+ "legacy-interactive" -> legacyInteractiveCmd+-- "nuke-caches" -> nukeCachesCmd _ -> fatalError $ "unknown command: `" ++ cmd ++ "'" newtype FatalError = FatalError String deriving (Show, Typeable)@@ -520,14 +478,18 @@ deriving (Show, Typeable) instance Exception InvalidCommandLine -exitError :: String -> IO a-exitError msg = hPutStrLn stderr msg >> exitFailure+exitError :: IOish m => String -> GhcModT m a+exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure +exitError' :: Options -> String -> IO a+exitError' opts msg =+ gmUnsafeErrStrLn opts (dropWhileEnd (=='\n') msg) >> liftIO exitFailure+ fatalError :: String -> a-fatalError s = throw $ FatalError $ progName ++ ": " ++ s+fatalError s = throw $ FatalError $ "ghc-mod: " ++ s withParseCmd :: IOish m- => [OptDescr (Options -> Options)]+ => [OptDescr (Options -> Either [String] Options)] -> ([String] -> GhcModT m a) -> [String] -> GhcModT m a@@ -535,25 +497,43 @@ (opts', rest) <- parseCommandArgs spec args <$> options withOptions (const opts') $ action rest +withParseCmd' :: (IOish m, ExceptionMonad m)+ => String+ -> [OptDescr (Options -> Either [String] Options)]+ -> ([String] -> GhcModT m a)+ -> [String]+ -> GhcModT m a+withParseCmd' cmd spec action args =+ catchArgs cmd $ withParseCmd spec action args++catchArgs :: (Monad m, ExceptionMonad m) => String -> m a -> m a+catchArgs cmd action =+ action `gcatch` \(PatternMatchFail _) ->+ throw $ InvalidCommandLine (Left cmd)+ modulesCmd, languagesCmd, flagsCmd, browseCmd, checkSyntaxCmd, expandTemplateCmd,- debugInfoCmd, infoCmd, typesCmd, splitsCmd, sigCmd, refineCmd, autoCmd,- findSymbolCmd, lintCmd, rootInfoCmd, pkgDocCmd, dumpSymbolCmd, bootCmd+ debugInfoCmd, componentInfoCmd, infoCmd, typesCmd, splitsCmd, sigCmd,+ refineCmd, autoCmd, findSymbolCmd, lintCmd, rootInfoCmd, pkgDocCmd,+ dumpSymbolCmd, bootCmd, legacyInteractiveCmd, nukeCachesCmd :: IOish m => [String] -> GhcModT m String -modulesCmd = withParseCmd [] $ \[] -> modules-languagesCmd = withParseCmd [] $ \[] -> languages-flagsCmd = withParseCmd [] $ \[] -> flags-debugInfoCmd = withParseCmd [] $ \[] -> debugInfo-rootInfoCmd = withParseCmd [] $ \[] -> rootInfo+modulesCmd = withParseCmd' "modules" s $ \[] -> modules+ where s = modulesArgSpec+languagesCmd = withParseCmd' "lang" [] $ \[] -> languages+flagsCmd = withParseCmd' "flag" [] $ \[] -> flags+debugInfoCmd = withParseCmd' "debug" [] $ \[] -> debugInfo+rootInfoCmd = withParseCmd' "root" [] $ \[] -> rootInfo+componentInfoCmd = withParseCmd' "debugComponent" [] $ \ts -> componentInfo ts -- internal-bootCmd = withParseCmd [] $ \[] -> boot+bootCmd = withParseCmd' "boot" [] $ \[] -> boot+nukeCachesCmd = withParseCmd' "nuke-caches" [] $ \[] -> nukeCaches >> return "" -dumpSymbolCmd = withParseCmd [] $ \[tmpdir] -> dumpSymbol tmpdir-findSymbolCmd = withParseCmd [] $ \[sym] -> findSymbol sym-pkgDocCmd = withParseCmd [] $ \[mdl] -> pkgDoc mdl-lintCmd = withParseCmd s $ \[file] -> lint file+dumpSymbolCmd = withParseCmd' "dump" [] $ \[tmpdir] -> dumpSymbol tmpdir+findSymbolCmd = withParseCmd' "find" [] $ \[sym] -> findSymbol sym+pkgDocCmd = withParseCmd' "doc" [] $ \[mdl] -> pkgDoc mdl+lintCmd = withParseCmd' "lint" s $ \[file] -> lint file where s = hlintArgSpec-browseCmd = withParseCmd s $ \mdls -> concat <$> browse `mapM` mdls+browseCmd = withParseCmd s $ \mdls -> concat <$> browse `mapM` mdls where s = browseArgSpec checkSyntaxCmd = withParseCmd [] $ checkAction checkSyntax expandTemplateCmd = withParseCmd [] $ checkAction expandTemplate@@ -565,10 +545,20 @@ refineCmd = withParseCmd [] $ locAction' "refine" refine infoCmd = withParseCmd [] $ action- where action [file,_,expr] = info file expr- action [file,expr] = info file expr+ where action [file,_,expr] = info file $ Expression expr+ action [file,expr] = info file $ Expression expr action _ = throw $ InvalidCommandLine (Left "info") +legacyInteractiveCmd = withParseCmd [] go+ where+ go [] =+ legacyInteractive >> return ""+ go ("help":[]) =+ return usage+ go ("version":[]) =+ return ghcModiVersion+ go _ = throw $ InvalidCommandLine (Left "legacy-interactive")+ checkAction :: ([t] -> a) -> [t] -> a checkAction _ [] = throw $ InvalidCommandLine (Right "No files given.") checkAction action files = action files@@ -578,22 +568,43 @@ locAction _ action [file, line,col] = action file (read line) (read col) locAction cmd _ _ = throw $ InvalidCommandLine (Left cmd) -locAction' :: String -> (String -> Int -> Int -> String -> a) -> [String] -> a-locAction' _ action [f,_,line,col,expr] = action f (read line) (read col) expr-locAction' _ action [f, line,col,expr] = action f (read line) (read col) expr+locAction' :: String -> (String -> Int -> Int -> Expression -> a) -> [String] -> a+locAction' _ action [f,_,line,col,expr] = action f (read line) (read col) (Expression expr)+locAction' _ action [f, line,col,expr] = action f (read line) (read col) (Expression expr) locAction' cmd _ _ = throw $ InvalidCommandLine (Left cmd) -hlintArgSpec :: [OptDescr (Options -> Options)]++modulesArgSpec :: [OptDescr (Options -> Either [String] Options)]+modulesArgSpec =+ [ option "d" ["detailed"] "Print package modules belong to." $+ NoArg $ \o -> Right $ o { detailed = True }+ ]+++hlintArgSpec :: [OptDescr (Options -> Either [String] Options)] hlintArgSpec = [ option "h" ["hlintOpt"] "Option to be passed to hlint" $- reqArg "hlintOpt" $ \h o -> o { hlintOpts = h : hlintOpts o }+ reqArg "hlintOpt" $ \h o -> Right $ o { hlintOpts = h : hlintOpts o } ]-browseArgSpec :: [OptDescr (Options -> Options)]++browseArgSpec :: [OptDescr (Options -> Either [String] Options)] browseArgSpec = [ option "o" ["operators"] "Also print operators." $- NoArg $ \o -> o { operators = True }+ NoArg $ \o -> Right $ o { operators = True } , option "d" ["detailed"] "Print symbols with accompanying signature." $- NoArg $ \o -> o { detailed = True }+ NoArg $ \o -> Right $ o { detailed = True } , option "q" ["qualified"] "Qualify symbols" $- NoArg $ \o -> o { qualified = True }+ NoArg $ \o -> Right $ o { qualified = True } ]++nukeCaches :: IOish m => GhcModT m ()+nukeCaches = do+ chdir <- liftIO $ (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod"+ c <- cradle++ when (cradleProjectType c == CabalProject) $ do+ let root = cradleRootDir c+ liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root </> "dist"]++trySome :: IO a -> IO (Either SomeException a)+trySome = try
src/GHCModi.hs view
@@ -1,262 +1,55 @@ {-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-} -- | WARNING--- This program in the process of being deprecated, use `ghc-mod --interactive`--- instead.---- Commands:--- check <file>--- find <symbol>--- info <file> <expr>--- type <file> <line> <column>--- lint [hlint options] <file>--- the format of hlint options is [String] because they may contain--- spaces and also <file> may contain spaces.--- boot--- browse [<package>:]<module>--- quit------ Session separators:--- OK -- success--- NG -- failure+-- This program is deprecated, use `ghc-mod legacy-interactive` instead. module Main where -import Config (cProjectVersion)-import Control.Applicative ((<$>))-import Control.Exception (SomeException(..))-import qualified Control.Exception as E-import Control.Monad (when)-import CoreMonad (liftIO)-import Data.List (intercalate)-import Data.List.Split (splitOn)-import Data.Version (showVersion)-import Language.Haskell.GhcMod-import Language.Haskell.GhcMod.Internal+import Control.Applicative+import Control.Monad+import Control.Exception+import Data.Version+import Data.Maybe+import System.IO+import System.Exit+import System.Process+import System.FilePath+import System.Environment import Paths_ghc_mod-import System.Console.GetOpt-import System.Directory (setCurrentDirectory)-import System.Environment (getArgs)-import System.Exit (ExitCode, exitFailure)-import System.IO (hFlush,stdout)--import Misc import Utils--------------------------------------------------------------------progVersion :: String-progVersion = "ghc-modi version " ++ showVersion version ++ " compiled by GHC " ++ cProjectVersion ++ "\n"--argspec :: [OptDescr (Options -> Options)]-argspec = [ Option "b" ["boundary"]- (ReqArg (\s opts -> opts { lineSeparator = LineSeparator s }) "sep")- "specify line separator (default is Nul string)"- , Option "l" ["tolisp"]- (NoArg (\opts -> opts { outputStyle = LispStyle }))- "print as a list of Lisp"- , Option "g" []- (ReqArg (\s opts -> opts { ghcUserOptions = s : ghcUserOptions opts }) "flag") "specify a ghc flag"- ]--usage :: String-usage = progVersion- ++ "Usage:\n"- ++ "\t ghc-modi [-l] [-b sep] [-g flag]\n"- ++ "\t ghc-modi version\n"- ++ "\t ghc-modi help\n"--parseArgs :: [OptDescr (Options -> Options)] -> [String] -> (Options, [String])-parseArgs spec argv- = case getOpt Permute spec argv of- (o,n,[] ) -> (foldr id defaultOptions o, n)- (_,_,errs) -> E.throw (CmdArg errs)---------------------------------------------------------------------- Running two GHC monad threads disables the handling of--- C-c since installSignalHandlers is called twice, sigh.+import Prelude main :: IO ()-main = E.handle cmdHandler $- go =<< parseArgs argspec <$> getArgs- where- cmdHandler (CmdArg _) = putStr $ usageInfo usage argspec- go (_,"help":_) = putStr $ usageInfo usage argspec- go (_,"version":_) = putStr progVersion- go (opt,_) = emptyNewUnGetLine >>= run opt--run :: Options -> UnGetLine -> IO ()-run opt ref = flip E.catches handlers $ do- cradle0 <- findCradle- let rootdir = cradleRootDir cradle0--- c = cradle0 { cradleCurrentDir = rootdir } TODO: ?????- setCurrentDirectory rootdir- prepareAutogen cradle0- -- Asynchronous db loading starts here.- symdbreq <- newSymDbReq opt- (res, _) <- runGhcModT opt $ do- crdl <- cradle- world <- liftIO $ getCurrentWorld crdl- loop symdbreq ref world- case res of- Right () -> return ()- Left (GMECabalConfigure msg) -> do- putStrLn $ notGood $ "cabal configure failed: " ++ show msg- exitFailure- Left e -> bug $ show e- where- -- this is just in case.- -- If an error is caught here, it is a bug of GhcMod library.- handlers = [ E.Handler (\(_ :: ExitCode) -> return ())- , E.Handler (\(_ :: Restart) -> run opt ref)- , E.Handler (\(SomeException e) -> bug $ show e) ]--bug :: String -> IO ()-bug msg = do- putStrLn $ notGood $ "BUG: " ++ msg- exitFailure--notGood :: String -> String-notGood msg = "NG " ++ escapeNewlines msg--escapeNewlines :: String -> String-escapeNewlines = replace "\n" "\\n" . replace "\\n" "\\\\n"--replace :: String -> String -> String -> String-replace needle replacement = intercalate replacement . splitOn needle--------------------------------------------------------------------loop :: IOish m => SymDbReq -> UnGetLine -> World -> GhcModT m ()-loop symdbreq ref world = do- -- blocking- cmdArg <- liftIO $ getCommand ref- -- after blocking, we need to see if the world has changed.- crdl <- cradle- changed <- liftIO $ didWorldChange world crdl- when changed $ do- liftIO $ ungetCommand ref cmdArg- E.throw Restart- cradle >>= liftIO . prepareAutogen- let (cmd,arg') = break (== ' ') cmdArg- arg = dropWhile (== ' ') arg'- (ret,ok) <- case cmd of- "check" -> checkStx arg- "find" -> findSym arg symdbreq- "lint" -> lintStx arg- "info" -> showInfo arg- "type" -> showType arg- "split" -> doSplit arg- "sig" -> doSig arg- "refine" -> doRefine arg- "auto" -> doAuto arg- "boot" -> bootIt- "browse" -> browseIt arg- "quit" -> return ("quit", False)- "" -> return ("quit", False)- _ -> return ([], True)- if ok then do- liftIO $ putStr ret- liftIO $ putStrLn "OK"- else do- liftIO $ putStrLn $ notGood ret- liftIO $ hFlush stdout- when ok $ loop symdbreq ref world--------------------------------------------------------------------checkStx :: IOish m => FilePath -> GhcModT m (String, Bool)-checkStx file = do- eret <- check [file]- case eret of- Right ret -> return (ret, True)- Left ret -> return (ret, True)--------------------------------------------------------------------findSym :: IOish m => Symbol -> SymDbReq -> GhcModT m (String, Bool)-findSym sym symdbreq = do- db <- getDb symdbreq >>= checkDb symdbreq- ret <- lookupSymbol sym db- return (ret, True)--lintStx :: IOish m => FilePath -> GhcModT m (String, Bool)-lintStx optFile = do- ret <- withOptions changeOpt $ lint file- return (ret, True)- where- (opts,file) = parseLintOptions optFile- hopts = if opts == "" then [] else read opts- changeOpt o = o { hlintOpts = hopts }---- |--- >>> parseLintOptions "[\"--ignore=Use camelCase\", \"--ignore=Eta reduce\"] file name"--- (["--ignore=Use camelCase", "--ignore=Eta reduce"], "file name")--- >>> parseLintOptions "file name"--- ([], "file name")-parseLintOptions :: String -> (String, String)-parseLintOptions optFile = case brk (== ']') (dropWhile (/= '[') optFile) of- ("","") -> ([], optFile)- (opt',file') -> (opt', dropWhile (== ' ') file')- where- brk _ [] = ([],[])- brk p (x:xs')- | p x = ([x],xs')- | otherwise = let (ys,zs) = brk p xs' in (x:ys,zs)--------------------------------------------------------------------showInfo :: IOish m => FilePath -> GhcModT m (String, Bool)-showInfo fileArg = do- let [file, expr] = splitN 2 fileArg- ret <- info file expr- return (ret, True)--showType :: IOish m => FilePath -> GhcModT m (String, Bool)-showType fileArg = do- let [file, line, column] = splitN 3 fileArg- ret <- types file (read line) (read column)- return (ret, True)--doSplit :: IOish m => FilePath -> GhcModT m (String, Bool)-doSplit fileArg = do- let [file, line, column] = splitN 3 fileArg- ret <- splits file (read line) (read column)- return (ret, True)--doSig :: IOish m => FilePath -> GhcModT m (String, Bool)-doSig fileArg = do- let [file, line, column] = splitN 3 fileArg- ret <- sig file (read line) (read column)- return (ret, True)--doRefine :: IOish m => FilePath -> GhcModT m (String, Bool)-doRefine fileArg = do- let [file, line, column, expr] = splitN 4 fileArg- ret <- refine file (read line) (read column) expr- return (ret, True)+main = do+ hPutStrLn stderr $+ "Warning: ghc-modi is deprecated please use 'ghc-mod legacy-interactive' instead" -doAuto :: IOish m => FilePath -> GhcModT m (String, Bool)-doAuto fileArg = do- let [file, line, column] = splitN 3 fileArg- ret <- auto file (read line) (read column)- return (ret, True)+ args <- getArgs+ bindir <- getBinDir+ let installedExe = bindir </> "ghc-mod"+ mexe <- mplus <$> mightExist installedExe <*> pathExe+ case mexe of+ Nothing -> do+ hPutStrLn stderr $+ "ghc-modi: Could not find '"++installedExe++"', check your installation!"+ exitWith $ ExitFailure 1 -----------------------------------------------------------------+ Just exe -> do+ (_, _, _, h) <-+ createProcess $ proc exe $ ["legacy-interactive"] ++ args+ exitWith =<< waitForProcess h -bootIt :: IOish m => GhcModT m (String, Bool)-bootIt = do- ret <- boot- return (ret, True)+pathExe :: IO (Maybe String)+pathExe = do+ ev <- try $ words <$> readProcess "ghc-mod" ["--version"] ""+ let mexe = case ev of+ Left (SomeException _) -> Nothing+ Right ["ghc-mod", "version", ver+ , "compiled", "by", "GHC", _]+ | showVersion version == ver -> do+ Just "ghc-mod"+ Right _ -> Nothing -browseIt :: IOish m => ModuleString -> GhcModT m (String, Bool)-browseIt mdl = do- let (det,rest') = break (== ' ') mdl- rest = dropWhile (== ' ') rest'- ret <- if det == "-d"- then withOptions setDetailed (browse rest)- else browse mdl- return (ret, True)- where- setDetailed opt = opt { detailed = True }+ when (isNothing mexe) $+ hPutStrLn stderr "ghc-modi: ghc-mod executable on PATH has different version, check your installation!"+ return mexe
src/Misc.hs view
@@ -1,75 +1,28 @@ {-# LANGUAGE DeriveDataTypeable, CPP #-} module Misc (- GHCModiError(..)- , Restart(..)- , UnGetLine- , emptyNewUnGetLine- , ungetCommand- , getCommand- , SymDbReq+ SymDbReq , newSymDbReq , getDb , checkDb- , prepareAutogen ) where -import Control.Applicative ((<$>))-import Control.Concurrent (threadDelay) import Control.Concurrent.Async (Async, async, wait)-import Control.Exception (Exception)-import Control.Monad (unless, when) import CoreMonad (liftIO) import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.List (isPrefixOf)-import Data.Maybe (isJust)-import Data.Typeable (Typeable)-import System.Directory (doesDirectoryExist, getDirectoryContents)-import System.IO (openBinaryFile, IOMode(..))-import System.Process+import Prelude import Language.Haskell.GhcMod-import Language.Haskell.GhcMod.Internal--------------------------------------------------------------------data GHCModiError = CmdArg [String] deriving (Show, Typeable)--instance Exception GHCModiError--------------------------------------------------------------------data Restart = Restart deriving (Show, Typeable)--instance Exception Restart--------------------------------------------------------------------newtype UnGetLine = UnGetLine (IORef (Maybe String))--emptyNewUnGetLine :: IO UnGetLine-emptyNewUnGetLine = UnGetLine <$> newIORef Nothing--ungetCommand :: UnGetLine -> String -> IO ()-ungetCommand (UnGetLine ref) cmd = writeIORef ref (Just cmd)--getCommand :: UnGetLine -> IO String-getCommand (UnGetLine ref) = do- mcmd <- readIORef ref- case mcmd of- Nothing -> getLine- Just cmd -> do- writeIORef ref Nothing- return cmd+import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO) ---------------------------------------------------------------- type SymDbReqAction = (Either GhcModError SymbolDb, GhcModLog) data SymDbReq = SymDbReq (IORef (Async SymDbReqAction)) (IO SymDbReqAction) -newSymDbReq :: Options -> IO SymDbReq-newSymDbReq opt = do- let act = runGhcModT opt loadSymbolDb+newSymDbReq :: Options -> FilePath -> IO SymDbReq+newSymDbReq opt dir = do+ let act = runGhcModT opt $ loadSymbolDb dir req <- async act ref <- newIORef req return $ SymDbReq ref act@@ -83,7 +36,7 @@ checkDb :: IOish m => SymDbReq -> SymbolDb -> GhcModT m SymbolDb checkDb (SymDbReq ref act) db = do- outdated <- liftIO $ isOutdated db+ outdated <- isOutdated db if outdated then do -- async and wait here is unnecessary because this is essentially -- synchronous. But Async can be used a cache.@@ -92,63 +45,3 @@ hoistGhcModT =<< liftIO (wait req) else return db--------------------------------------------------------------------build :: IO ProcessHandle-build = do-#ifdef WINDOWS- nul <- openBinaryFile "NUL" AppendMode-#else- nul <- openBinaryFile "/dev/null" AppendMode-#endif- (_, _, _, hdl) <- createProcess $ pro nul- return hdl- where- pro nul = CreateProcess {- cmdspec = RawCommand "cabal" ["build"]- , cwd = Nothing- , env = Nothing- , std_in = Inherit- , std_out = UseHandle nul- , std_err = UseHandle nul- , close_fds = False-#if __GLASGOW_HASKELL__ >= 702- , create_group = True-#endif-#if __GLASGOW_HASKELL__ >= 707- , delegate_ctlc = False-#endif- }--autogen :: String-autogen = "dist/build/autogen"--isAutogenPrepared :: IO Bool-isAutogenPrepared = do- exist <- doesDirectoryExist autogen- if exist then do- files <- filter ("." `isPrefixOf`) <$> getDirectoryContents autogen- if length files >= 2 then- return True- else- return False- else- return False--watch :: Int -> ProcessHandle -> IO ()-watch 0 _ = return ()-watch n hdl = do- prepared <- isAutogenPrepared- if prepared then- interruptProcessGroupOf hdl- else do- threadDelay 100000- watch (n - 1) hdl--prepareAutogen :: Cradle -> IO ()-prepareAutogen crdl = when (isJust $ cradleCabalFile crdl) $ do- prepared <- isAutogenPrepared- unless prepared $ do- hdl <- build- watch 30 hdl
− src/Utils.hs
@@ -1,27 +0,0 @@-module Utils where---- |------ >>> split "foo bar baz"--- ["foo","bar baz"]--- >>> split "foo bar baz"--- ["foo","bar baz"]-split :: String -> [String]-split xs = [ys, dropWhile isSpace zs]- where- isSpace = (== ' ')- (ys,zs) = break isSpace xs---- |------ >>> splitN 0 "foo bar baz"--- ["foo","bar baz"]--- >>> splitN 2 "foo bar baz"--- ["foo","bar baz"]--- >>> splitN 3 "foo bar baz"--- ["foo","bar","baz"]-splitN :: Int -> String -> [String]-splitN n xs- | n <= 2 = split xs- | otherwise = let [ys,zs] = split xs- in ys : splitN (n - 1) zs
test/BrowseSpec.hs view
@@ -26,7 +26,8 @@ syms `shouldContain` ["Left :: a -> Either a b"] describe "`browse' in a project directory" $ do- it "lists symbols defined in a a local module (e.g. `Baz.baz)" $ do- withDirectory_ "test/data" $ do- syms <- runID $ lines <$> browse "Baz"- syms `shouldContain` ["baz"]+ it "can list symbols defined in a a local module" $ do+ withDirectory_ "test/data/ghc-mod-check/lib" $ do+ syms <- runD $ lines <$> browse "Data.Foo"+ syms `shouldContain` ["foo"]+ syms `shouldContain` ["fibonacci"]
− test/CabalApiSpec.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--module CabalApiSpec where--import Control.Applicative-import Language.Haskell.GhcMod.CabalApi-import Language.Haskell.GhcMod.Cradle-import Language.Haskell.GhcMod.Types-import Test.Hspec-import System.Directory-import System.FilePath-import System.Process (readProcess)--import Dir-import TestUtils--import Config (cProjectVersionInt) -- ghc version--ghcVersion :: Int-ghcVersion = read cProjectVersionInt--spec :: Spec-spec = do- describe "parseCabalFile" $ do- it "throws an exception if the cabal file is broken" $ do- shouldReturnError $ do- withDirectory_ "test/data/broken-cabal" $ do- crdl <- findCradle- runD' $ parseCabalFile crdl "broken.cabal"--- describe "getCompilerOptions" $ do- it "gets necessary CompilerOptions" $ do- cwd <- getCurrentDirectory- withDirectory "test/data/subdir1/subdir2" $ \dir -> do- crdl <- findCradle- let Just cabalFile = cradleCabalFile crdl- pkgDesc <- runD $ parseCabalFile crdl cabalFile- res <- runD $ getCompilerOptions [] crdl pkgDesc- let res' = res {- ghcOptions = ghcOptions res- , includeDirs = map (toRelativeDir dir) (includeDirs res)- }- if ghcVersion < 706- then ghcOptions res' `shouldContain` ["-global-package-conf", "-no-user-package-conf","-package-conf",cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d","-XHaskell98"]- else ghcOptions res' `shouldContain` ["-global-package-db", "-no-user-package-db","-package-db",cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d","-XHaskell98"]- includeDirs res' `shouldBe` ["test/data","test/data/dist/build","test/data/dist/build/autogen","test/data/subdir1/subdir2","test/data/test"]- (pkgName `map` depPackages res') `shouldContain` ["Cabal"]--- describe "cabalDependPackages" $ do- it "extracts dependent packages" $ do- crdl <- findCradle' "test/data/"- pkgs <- cabalDependPackages . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- pkgs `shouldBe` ["Cabal","base","template-haskell"]- it "uses non default flags" $ do- withDirectory_ "test/data/cabal-flags" $ do- crdl <- findCradle- _ <- readProcess "cabal" ["configure", "-ftest-flag"] ""- pkgs <- cabalDependPackages . cabalAllBuildInfo <$> runD (parseCabalFile crdl "cabal-flags.cabal")- pkgs `shouldBe` ["Cabal","base"]-- describe "cabalSourceDirs" $ do- it "extracts all hs-source-dirs" $ do- crdl <- findCradle' "test/data/check-test-subdir"- dirs <- cabalSourceDirs . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/check-test-subdir/check-test-subdir.cabal")- dirs `shouldBe` ["src", "test"]- it "extracts all hs-source-dirs including \".\"" $ do- crdl <- findCradle' "test/data/"- dirs <- cabalSourceDirs . cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- dirs `shouldBe` [".", "test"]-- describe "cabalAllBuildInfo" $ do- it "extracts build info" $ do- crdl <- findCradle' "test/data/"- info <- cabalAllBuildInfo <$> runD (parseCabalFile crdl "test/data/cabalapi.cabal")- show info `shouldBe` "[BuildInfo {buildable = True, buildTools = [], cppOptions = [], ccOptions = [], ldOptions = [], pkgconfigDepends = [], frameworks = [], cSources = [], hsSourceDirs = [\".\"], otherModules = [ModuleName [\"Browse\"],ModuleName [\"CabalApi\"],ModuleName [\"Cabal\"],ModuleName [\"CabalDev\"],ModuleName [\"Check\"],ModuleName [\"ErrMsg\"],ModuleName [\"Flag\"],ModuleName [\"GHCApi\"],ModuleName [\"GHCChoice\"],ModuleName [\"Gap\"],ModuleName [\"Info\"],ModuleName [\"Lang\"],ModuleName [\"Lint\"],ModuleName [\"List\"],ModuleName [\"Paths_ghc_mod\"],ModuleName [\"Types\"]], defaultLanguage = Nothing, otherLanguages = [], defaultExtensions = [], otherExtensions = [], oldExtensions = [], extraLibs = [], extraLibDirs = [], includeDirs = [], includes = [], installIncludes = [], options = [(GHC,[\"-Wall\"])], ghcProfOptions = [], ghcSharedOptions = [], customFieldsBI = [], targetBuildDepends = [Dependency (PackageName \"Cabal\") (UnionVersionRanges (ThisVersion (Version {versionBranch = [1,10], versionTags = []})) (LaterVersion (Version {versionBranch = [1,10], versionTags = []}))),Dependency (PackageName \"base\") (IntersectVersionRanges (UnionVersionRanges (ThisVersion (Version {versionBranch = [4,0], versionTags = []})) (LaterVersion (Version {versionBranch = [4,0], versionTags = []}))) (EarlierVersion (Version {versionBranch = [5], versionTags = []}))),Dependency (PackageName \"template-haskell\") AnyVersion]},BuildInfo {buildable = True, buildTools = [], cppOptions = [], ccOptions = [], ldOptions = [], pkgconfigDepends = [], frameworks = [], cSources = [], hsSourceDirs = [\"test\",\".\"], otherModules = [ModuleName [\"Expectation\"],ModuleName [\"BrowseSpec\"],ModuleName [\"CabalApiSpec\"],ModuleName [\"FlagSpec\"],ModuleName [\"LangSpec\"],ModuleName [\"LintSpec\"],ModuleName [\"ListSpec\"]], defaultLanguage = Nothing, otherLanguages = [], defaultExtensions = [], otherExtensions = [], oldExtensions = [], extraLibs = [], extraLibDirs = [], includeDirs = [], includes = [], installIncludes = [], options = [], ghcProfOptions = [], ghcSharedOptions = [], customFieldsBI = [], targetBuildDepends = [Dependency (PackageName \"Cabal\") (UnionVersionRanges (ThisVersion (Version {versionBranch = [1,10], versionTags = []})) (LaterVersion (Version {versionBranch = [1,10], versionTags = []}))),Dependency (PackageName \"base\") (IntersectVersionRanges (UnionVersionRanges (ThisVersion (Version {versionBranch = [4,0], versionTags = []})) (LaterVersion (Version {versionBranch = [4,0], versionTags = []}))) (EarlierVersion (Version {versionBranch = [5], versionTags = []})))]}]"
test/CheckSpec.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE CPP #-} module CheckSpec where -import Data.List (isSuffixOf, isInfixOf, isPrefixOf) import Language.Haskell.GhcMod-import System.FilePath++import Data.List+import System.Process import Test.Hspec import TestUtils@@ -14,38 +15,55 @@ describe "checkSyntax" $ do it "works even if an executable depends on the library defined in the same cabal file" $ do withDirectory_ "test/data/ghc-mod-check" $ do- res <- runID $ checkSyntax ["main.hs"]+ res <- runD $ checkSyntax ["main.hs"] res `shouldBe` "main.hs:5:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "works even if a module imports another module from a different directory" $ do withDirectory_ "test/data/check-test-subdir" $ do- res <- runID $ checkSyntax ["test/Bar/Baz.hs"]+ _ <- system "cabal configure --enable-tests"+ res <- runD $ checkSyntax ["test/Bar/Baz.hs"] res `shouldSatisfy` (("test" </> "Foo.hs:3:1:Warning: Top-level binding with no type signature: foo :: [Char]\n") `isSuffixOf`) it "detects cyclic imports" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["Mutual1.hs"]+ withDirectory_ "test/data/import-cycle" $ do+ res <- runD $ checkSyntax ["Mutual1.hs"] res `shouldSatisfy` ("Module imports form a cycle" `isInfixOf`) it "works with modules using QuasiQuotes" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["Baz.hs"]- res `shouldSatisfy` ("Baz.hs:5:1:Warning:" `isPrefixOf`)+ withDirectory_ "test/data/quasi-quotes" $ do+ res <- runD $ checkSyntax ["QuasiQuotes.hs"]+ res `shouldSatisfy` ("QuasiQuotes.hs:6:1:Warning:" `isInfixOf`) #if __GLASGOW_HASKELL__ >= 708 it "works with modules using PatternSynonyms" $ do withDirectory_ "test/data/pattern-synonyms" $ do- res <- runID $ checkSyntax ["B.hs"]+ res <- runD $ checkSyntax ["B.hs"] res `shouldSatisfy` ("B.hs:6:9:Warning:" `isPrefixOf`) #endif it "works with foreign exports" $ do- withDirectory_ "test/data" $ do- res <- runID $ checkSyntax ["ForeignExport.hs"]+ withDirectory_ "test/data/foreign-export" $ do+ res <- runD $ checkSyntax ["ForeignExport.hs"] res `shouldBe` "" context "when no errors are found" $ do it "doesn't output an empty line" $ do- withDirectory_ "test/data/ghc-mod-check/Data" $ do- res <- runID $ checkSyntax ["Foo.hs"]+ withDirectory_ "test/data/ghc-mod-check/lib/Data" $ do+ res <- runD $ checkSyntax ["Foo.hs"] res `shouldBe` ""++#if __GLASGOW_HASKELL__ >= 708+-- See https://github.com/kazu-yamamoto/ghc-mod/issues/507+ it "emits warnings generated in GHC's desugar stage" $ do+ withDirectory_ "test/data/check-missing-warnings" $ do+ res <- runD $ checkSyntax ["DesugarWarnings.hs"]+ res `shouldBe` "DesugarWarnings.hs:4:9:Warning: Pattern match(es) are non-exhaustive\NULIn a case alternative: Patterns not matched: _ : _\n"+#endif++ it "works with cabal builtin preprocessors" $ do+ withDirectory_ "test/data/cabal-preprocessors" $ do+ _ <- system "cabal clean"+ _ <- system "cabal build"+ res <- runD $ checkSyntax ["Main.hs"]+ res `shouldBe` "Preprocessed.hsc:3:1:Warning: Top-level binding with no type signature: warning :: ()\n"
test/Dir.hs view
@@ -1,9 +1,15 @@-module Dir where+module Dir (+ module Dir+ , getCurrentDirectory+ , (</>)+ ) where import Control.Exception as E import Data.List (isPrefixOf) import System.Directory-import System.FilePath (addTrailingPathSeparator)+import System.FilePath (addTrailingPathSeparator,(</>))++ withDirectory_ :: FilePath -> IO a -> IO a withDirectory_ dir action = bracket getCurrentDirectory
+ test/HomeModuleGraphSpec.hs view
@@ -0,0 +1,178 @@+-- ghc-mod: Making Haskell development *more* fun+-- Copyright (C) 2015 Daniel Gröber <dxld ÄT darkboxed DOT org>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE OverloadedStrings #-}++module HomeModuleGraphSpec where++import Language.Haskell.GhcMod.HomeModuleGraph+import Language.Haskell.GhcMod.Target+import TestUtils++import GHC+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Maybe++import Test.Hspec++runAGhc :: [GHCOption] -> (HscEnv -> LightGhc a) -> IO a+runAGhc opts action = withLightHscEnv opts $ \env -> do+ runLightGhc env $ getSession >>= action++hmGraph :: FilePath -> [String] -> String -> IO GmModuleGraph+hmGraph dir opts mn = runAGhc opts $ \env -> liftIO $ do+ runD' dir $ do+ smp <- liftIO $ findModulePathSet env [mkModuleName mn]+ homeModuleGraph env smp++uhmGraph :: FilePath -> [String] -> String -> String -> GmModuleGraph -> IO GmModuleGraph+uhmGraph dir opts mn umn g = runAGhc opts $ \env -> liftIO $ do+ runD' dir $ do+ smp <- liftIO $ findModulePathSet env [mkModuleName mn]+ usmp <- liftIO $ findModulePathSet env [mkModuleName umn]+ updateHomeModuleGraph env g smp usmp++mapMap :: (Ord k, Ord k')+ => (k -> k') -> (a -> a') -> Map.Map k a -> Map.Map k' a'+mapMap fk fa = Map.mapKeys fk . Map.map fa++mapMpFn :: (FilePath -> FilePath) -> ModulePath -> ModulePath+mapMpFn f (ModulePath mn fn) = ModulePath mn (f fn)++mp :: ModuleName -> ModulePath+mp mn = ModulePath mn $ moduleNameString mn ++ ".hs"++spec :: Spec+spec = do+ describe "reachable" $ do+ let+ smp =+ Set.fromList+ [ mp "A"+ , mp "B"+ , mp "C"+ , mp "D"+ , mp "E"+ , mp "F"+ , mp "G"+ , mp "H"+ , mp "I"+ ]++ moduleMap = mkModuleMap smp++ completeGraph =+ Map.map (Set.map lookupMM) . Map.mapKeys lookupMM++ lookupMM = fromJust . flip Map.lookup moduleMap++ graph = completeGraph $+ Map.fromList+ [ ("A", Set.fromList ["B"])+ , ("B", Set.fromList ["C", "D"])+ , ("C", Set.fromList ["F"])+ , ("D", Set.fromList ["E"])+ , ("E", Set.fromList [])+ , ("F", Set.fromList [])+ , ("G", Set.fromList [])+ , ("H", Set.fromList [])+ , ("I", Set.fromList [])+ ]++ really_reachable =+ Set.fromList+ [ mp "A"+ , mp "B"+ , mp "C"+ , mp "D"+ , mp "E"+ , mp "F"+ ]++ g = GmModuleGraph {+ gmgGraph = graph+ }++ it "reachable Set.empty g == Set.empty" $ do+ reachable Set.empty g `shouldBe` Set.empty++ it "lists only reachable nodes" $ do+ reachable (Set.fromList [mp "A"]) g `shouldBe` really_reachable+++ describe "homeModuleGraph" $ do+ it "cycles don't break it" $ do+ let tdir = "test/data/home-module-graph/cycle"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [mp "A"])+ ]++ it "follows imports" $ do+ let tdir = "test/data/home-module-graph/indirect"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList [mp "B"])+ , (mp "A2", Set.fromList [mp "C"])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ , (mp "C", Set.fromList [])+ ]++ it "returns partial results on parse errors" $ do+ let tdir = "test/data/home-module-graph/errors"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList []) -- parse error here+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ ]++ it "returns partial results on CPP errors" $ do+ let tdir = "test/data/home-module-graph/cpp"+ g <- hmGraph tdir [] "A"+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList []) -- CPP error here+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ ]++ describe "updateHomeModuleGraph" $ do+ it "removes unreachable nodes" $ do+ let tdir = "test/data/home-module-graph/indirect"+ let tdir' = "test/data/home-module-graph/indirect-update"+ ig <- hmGraph tdir [] "A"+ g <- uhmGraph tdir' [] "A" "A2" ig+ gmgGraph g `shouldBe`+ Map.fromList+ [ (mp "A", Set.fromList [mp "A1", mp "A2", mp "A3"])+ , (mp "A1", Set.fromList [mp "B"])+ , (mp "A2", Set.fromList [])+ , (mp "A3", Set.fromList [mp "B"])+ , (mp "B", Set.fromList [])+ -- C was removed+ ]
test/InfoSpec.hs view
@@ -9,51 +9,43 @@ #else import System.Environment (getExecutablePath) #endif-import System.Exit import System.FilePath-import System.Process import Test.Hspec import TestUtils-import Dir spec :: Spec spec = do describe "types" $ do it "shows types of the expression and its outers" $ do- withDirectory_ "test/data/ghc-mod-check" $ do- res <- runD $ types "Data/Foo.hs" 9 5- res `shouldBe` "9 5 11 40 \"Int -> a -> a -> a\"\n7 1 11 40 \"Int -> Integer\"\n"+ let tdir = "test/data/ghc-mod-check"+ res <- runD' tdir $ types "lib/Data/Foo.hs" 9 5+ res `shouldBe` "9 5 11 40 \"Int -> a -> a -> a\"\n7 1 11 40 \"Int -> Integer\"\n" it "works with a module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ types "Bar.hs" 5 1- res `shouldBe` unlines ["5 1 5 20 \"[Char]\""]+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ types "Bar.hs" 5 1+ res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] it "works with a module that imports another module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ types "Main.hs" 3 8- res `shouldBe` unlines ["3 8 3 16 \"String -> IO ()\"", "3 8 3 20 \"IO ()\"", "3 1 3 20 \"IO ()\""]+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ types "ImportsTH.hs" 3 8+ res `shouldBe` unlines ["3 8 3 16 \"String -> IO ()\"", "3 8 3 20 \"IO ()\"", "3 1 3 20 \"IO ()\""] describe "info" $ do- it "works for non-export functions" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Info.hs" "fib"- res `shouldSatisfy` ("fib :: Int -> Int" `isPrefixOf`)+ it "works for non exported functions" $ do+ let tdir = "test/data/non-exported"+ res <- runD' tdir $ info "Fib.hs" $ Expression "fib"+ res `shouldSatisfy` ("fib :: Int -> Int" `isPrefixOf`) it "works with a module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Bar.hs" "foo"- res `shouldSatisfy` ("foo :: ExpQ" `isPrefixOf`)+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ info "Bar.hs" $ Expression "foo"+ res `shouldSatisfy` ("foo :: ExpQ" `isPrefixOf`) it "works with a module that imports another module using TemplateHaskell" $ do- withDirectory_ "test/data" $ do- res <- runD $ info "Main.hs" "bar"- res `shouldSatisfy` ("bar :: [Char]" `isPrefixOf`)-- it "doesn't fail on unicode output" $ do- dir <- getDistDir- code <- rawSystem (dir </> "build/ghc-mod/ghc-mod") ["info", "test/data/Unicode.hs", "Unicode", "unicode"]- code `shouldSatisfy` (== ExitSuccess)+ let tdir = "test/data/template-haskell"+ res <- runD' tdir $ info "ImportsTH.hs" $ Expression "bar"+ res `shouldSatisfy` ("bar :: [Char]" `isPrefixOf`) getDistDir :: IO FilePath getDistDir = takeDirectory . takeDirectory . takeDirectory <$> getExecutablePath
test/LintSpec.hs view
@@ -8,10 +8,10 @@ spec = do describe "lint" $ do it "can detect a redundant import" $ do- res <- runD $ lint "test/data/hlint.hs"- res `shouldBe` "test/data/hlint.hs:4:8: Error: Redundant do\NULFound:\NUL do putStrLn \"Hello, world!\"\NULWhy not:\NUL putStrLn \"Hello, world!\"\n"+ res <- runD $ lint "test/data/hlint/hlint.hs"+ res `shouldBe` "test/data/hlint/hlint.hs:4:8: Error: Redundant do\NULFound:\NUL do putStrLn \"Hello, world!\"\NULWhy not:\NUL putStrLn \"Hello, world!\"\n" context "when no suggestions are given" $ do it "doesn't output an empty line" $ do- res <- runD $ lint "test/data/ghc-mod-check/Data/Foo.hs"+ res <- runD $ lint "test/data/ghc-mod-check/lib/Data/Foo.hs" res `shouldBe` ""
test/Main.hs view
@@ -4,6 +4,7 @@ import Control.Exception as E import Control.Monad (void)+import Data.List import Language.Haskell.GhcMod (debugInfo) import System.Process import Test.Hspec@@ -11,22 +12,38 @@ main :: IO () main = do- let sandboxes = [ "test/data", "test/data/check-packageid"+ let sandboxes = [ "test/data/cabal-project"+ , "test/data/check-packageid" , "test/data/duplicate-pkgver/" , "test/data/broken-cabal/" ] genSandboxCfg dir = withDirectory dir $ \cwdir -> do system ("sed 's|@CWD@|" ++ cwdir ++ "|g' cabal.sandbox.config.in > cabal.sandbox.config") pkgDirs =- [ "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"+ [ "test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d" , "test/data/check-packageid/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d" , "test/data/duplicate-pkgver/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"] genGhcPkgCache dir = system $ "ghc-pkg recache --force -f" ++ dir+ genSandboxCfg `mapM_` sandboxes genGhcPkgCache `mapM_` pkgDirs- void $ system "find test -name setup-config -name ghc-mod.cache -exec rm {} \\;"++ let caches = [ "setup-config"+ , "setup-config.ghc-mod.cabal-helper"+ , "setup-config.ghc-mod.cabal-components"+ , "setup-config.ghc-mod.resolved-components"+ , "setup-config.ghc-mod.package-options"+ , "setup-config.ghc-mod.package-db-stack"+ , "ghc-mod.cache"+ ]+ cachesFindExp :: String+ cachesFindExp = unwords $ intersperse "-o " $ map ("-name "++) caches++ cleanCmd = "find test \\( "++ cachesFindExp ++" \\) -exec rm {} \\;"++ putStrLn $ "$ " ++ cleanCmd+ void $ system cleanCmd void $ system "cabal --version"- putStrLn $ "ghc-mod was built with Cabal version " ++ VERSION_Cabal void $ system "ghc --version" (putStrLn =<< runD debugInfo)
test/MonadSpec.hs view
@@ -1,39 +1,17 @@-{-# LANGUAGE ScopedTypeVariables #-} module MonadSpec where import Test.Hspec-import Dir import TestUtils-import Control.Applicative-import Control.Exception import Control.Monad.Error.Class spec :: Spec spec = do describe "When using GhcModT in a do block" $ it "a pattern match failure causes a call to `fail` on ErrorT in the monad stack" $ do- (a, _)+ (a, _h) <- runGhcModT defaultOptions $ do Just _ <- return Nothing return "hello" `catchError` (const $ fail "oh noes") a `shouldBe` (Left $ GMEString "oh noes")-- describe "runGhcModT" $- it "complains if the cabal file fails to parse while a sandbox is present" $ withDirectory_ "test/data/broken-cabal" $ do- shouldReturnError $ runD' (gmCradle <$> ask)-- describe "gmsGet/Put" $- it "work" $ do- (runD $ gmsPut (GhcModState Intelligent) >> gmsGet)- `shouldReturn` (GhcModState Intelligent)-- describe "liftIO" $ do- it "converts user errors to GhcModError" $ do- shouldReturnError $- runD' $ liftIO $ throw (userError "hello") >> return ""-- it "converts a file not found exception to GhcModError" $ do- shouldReturnError $- runD' $ liftIO $ readFile "/DOES_NOT_EXIST" >> return ""
test/PathsAndFilesSpec.hs view
@@ -1,42 +1,33 @@-{-# LANGUAGE CPP #-} module PathsAndFilesSpec where import Language.Haskell.GhcMod.PathsAndFiles-#if __GLASGOW_HASKELL__ <= 706-import Language.Haskell.GhcMod.Types-import Language.Haskell.GhcMod.GhcPkg-#endif import System.Directory-import System.Environment-import System.FilePath ((</>))+import System.FilePath import Test.Hspec+import TestUtils spec :: Spec spec = do describe "getSandboxDb" $ do--- ghc < 7.8-#if __GLASGOW_HASKELL__ <= 706- it "does include a sandbox with ghc < 7.8" $ do- cwd <- getCurrentDirectory- getPackageDbStack "test/data/" `shouldReturn` [GlobalDb, PackageDb $ cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"]-#endif- it "can parse a config file and extract the sandbox package-db" $ do cwd <- getCurrentDirectory- pkgDb <- getSandboxDb "test/data/"- pkgDb `shouldBe` Just (cwd </> "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d")+ Just db <- getSandboxDb "test/data/cabal-project"+ db `shouldSatisfy` isPkgDbAt (cwd </> "test/data/cabal-project/.cabal-sandbox") it "returns Nothing if the sandbox config file is broken" $ do getSandboxDb "test/data/broken-sandbox" `shouldReturn` Nothing - describe "getCabalFiles" $ do- it "doesn't think $HOME/.cabal is a cabal file" $ do- (getCabalFiles =<< getEnv "HOME") `shouldReturn` []- describe "findCabalFile" $ do it "works" $ do- findCabalFile "test/data" `shouldReturn` Just "test/data/cabalapi.cabal"+ findCabalFile "test/data/cabal-project" `shouldReturn` Just "test/data/cabal-project/cabalapi.cabal" it "finds cabal files in parent directories" $ do- findCabalFile "test/data/subdir1/subdir2" `shouldReturn` Just "test/data/cabalapi.cabal"+ findCabalFile "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just "test/data/cabal-project/cabalapi.cabal"++ describe "findCabalSandboxDir" $ do+ it "works" $ do+ findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just "test/data/cabal-project"++ it "finds sandboxes in parent directories" $ do+ findCabalSandboxDir "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just "test/data/cabal-project"
test/TestUtils.hs view
@@ -1,27 +1,37 @@+{-# OPTIONS_GHC -fno-warn-orphans #-} module TestUtils ( run , runD , runD'- , runI- , runID- , runIsolatedGhcMod- , isolateCradle+ , runE+ , runNullLog , shouldReturnError+ , isPkgDbAt+ , isPkgConfDAt , module Language.Haskell.GhcMod.Monad , module Language.Haskell.GhcMod.Types ) where +import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Monad+import Language.Haskell.GhcMod.Cradle import Language.Haskell.GhcMod.Types +import Control.Arrow+import Control.Applicative+import Control.Monad.Error (ErrorT, runErrorT)+import Control.Monad.Trans.Journal+import Data.List.Split+import Data.String+import System.FilePath+import System.Directory import Test.Hspec -isolateCradle :: IOish m => GhcModT m a -> GhcModT m a-isolateCradle action =- local modifyEnv $ action- where- modifyEnv e = e { gmCradle = (gmCradle e) { cradlePkgDbStack = [GlobalDb] } }+import Exception +testLogLevel :: GmLogLevel+testLogLevel = GmDebug+ extract :: Show e => IO (Either e a, w) -> IO a extract action = do (r,_) <- action@@ -29,29 +39,47 @@ Right a -> return a Left e -> error $ show e -runIsolatedGhcMod :: Options -> GhcModT IO a -> IO a-runIsolatedGhcMod opt action = do- extract $ runGhcModT opt $ isolateCradle action+withSpecCradle :: IOish m => FilePath -> (Cradle -> m a) -> m a+withSpecCradle cradledir f =+ gbracket (liftIO $ findSpecCradle cradledir) (liftIO . cleanupCradle) f --- | Run GhcMod in isolated cradle with default options-runID :: GhcModT IO a -> IO a-runID = runIsolatedGhcMod defaultOptions+withGhcModEnvSpec :: IOish m => FilePath -> Options -> (GhcModEnv -> m a) -> m a+withGhcModEnvSpec dir opt f = withSpecCradle dir $ withGhcModEnv' opt f --- | Run GhcMod in isolated cradle-runI :: Options -> GhcModT IO a -> IO a-runI = runIsolatedGhcMod+runGhcModTSpec :: Options -> GhcModT IO a -> IO (Either GhcModError a, GhcModLog)+runGhcModTSpec opt action = do+ dir <- getCurrentDirectory+ runGhcModTSpec' dir opt action +runGhcModTSpec' :: IOish m+ => FilePath -> Options -> GhcModT m b -> m (Either GhcModError b, GhcModLog)+runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' ->+ withGhcModEnvSpec dir' opt $ \env -> do+ first (fst <$>) <$> runGhcModT'' env defaultGhcModState+ (gmSetLogLevel (logLevel opt) >> action)+ -- | Run GhcMod run :: Options -> GhcModT IO a -> IO a-run opt a = extract $ runGhcModT opt a+run opt a = extract $ runGhcModTSpec opt a -- | Run GhcMod with default options runD :: GhcModT IO a -> IO a-runD = extract . runGhcModT defaultOptions+runD =+ extract . runGhcModTSpec defaultOptions { logLevel = testLogLevel } -runD' :: GhcModT IO a -> IO (Either GhcModError a, GhcModLog)-runD' = runGhcModT defaultOptions+runD' :: FilePath -> GhcModT IO a -> IO a+runD' dir =+ extract . runGhcModTSpec' dir defaultOptions { logLevel = testLogLevel } +runE :: ErrorT e IO a -> IO (Either e a)+runE = runErrorT++runNullLog :: MonadIO m => JournalT GhcModLog m a -> m a+runNullLog action = do+ (a,w) <- runJournalT action+ liftIO $ print w+ return a+ shouldReturnError :: Show a => IO (Either GhcModError a, GhcModLog) -> Expectation@@ -61,3 +89,21 @@ where isLeft (Left _) = True isLeft _ = False++isPkgConfD :: FilePath -> Bool+isPkgConfD d = let+ (_dir, pkgconfd) = splitFileName d+ in case splitOn "-" pkgconfd of+ [_arch, _platform, _compiler, _compver, "packages.conf.d"] -> True+ _ -> False++isPkgConfDAt :: FilePath -> FilePath -> Bool+isPkgConfDAt d d' | d == takeDirectory d' && isPkgConfD d' = True+isPkgConfDAt _ _ = False++isPkgDbAt :: FilePath -> GhcPkgDb -> Bool+isPkgDbAt d (PackageDb dir) = isPkgConfDAt d dir+isPkgDbAt _ _ = False++instance IsString ModuleName where+ fromString = mkModuleName
− test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf
@@ -1,4 +0,0 @@-name: Cabal-version: 1.18.1.3-id: Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b-exposed: True
− test/data/.cabal-sandbox/packages/00-index.tar
binary file changed (10240 → absent bytes)
− test/data/Bar.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Bar (bar) where-import Foo (foo)--bar = $foo ++ "bar"
− test/data/Baz.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE QuasiQuotes #-}-module Baz (baz) where-import Foo (fooQ)--baz = [fooQ| foo bar baz |]
− test/data/Foo.hs
@@ -1,9 +0,0 @@-module Foo (foo, fooQ) where-import Language.Haskell.TH-import Language.Haskell.TH.Quote (QuasiQuoter(..))--foo :: ExpQ-foo = stringE "foo"--fooQ :: QuasiQuoter-fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
− test/data/ForeignExport.hs
@@ -1,10 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-}--module ForeignExport where--import Foreign.C.Types--foreign export ccall foo :: CUInt--foo :: CUInt-foo = 123
− test/data/Info.hs
@@ -1,8 +0,0 @@-{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted--module Info () where--fib :: Int -> Int-fib 0 = 0-fib 1 = 1-fib n = fib (n - 1) + fib (n - 2)
− test/data/Main.hs
@@ -1,3 +0,0 @@-import Bar (bar)--main = putStrLn bar
− test/data/Mutual1.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted--module Mutual1 where--import Mutual2
− test/data/Mutual2.hs
@@ -1,3 +0,0 @@-module Mutual2 where--import Mutual1
− test/data/Unicode.hs
@@ -1,4 +0,0 @@-module Unicode where--unicode :: α -> α-unicode = id
+ test/data/annotations/With.hs view
@@ -0,0 +1,6 @@+module Main where++{-# ANN module ["this", "can", "be", "anything"] #-}++main :: IO ()+main = putStrLn "Hello world!"
test/data/cabal-flags/cabal-flags.cabal view
@@ -7,8 +7,7 @@ default: False library- build-depends: base == 4.*+ build-depends: base if flag(test-flag) build-depends: Cabal >= 1.10-
+ test/data/cabal-preprocessors/Main.hs view
@@ -0,0 +1,4 @@+import Preprocessed++main :: IO ()+main = return warning
+ test/data/cabal-preprocessors/Preprocessed.hsc view
@@ -0,0 +1,3 @@+module Preprocessed where++warning = ()
+ test/data/cabal-preprocessors/cabal-preprocessors.cabal view
@@ -0,0 +1,14 @@+name: cabal-preprocessors+version: 0.1.0.0+license-file: LICENSE+author: asd+maintainer: asd+build-type: Simple+cabal-version: >=1.10++executable cabal-preprocessors+ main-is: Main.hs+ build-depends: base+ default-language: Haskell2010+ other-modules: Preprocessed+ ghc-options: -Wall
+ test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d/Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b.conf view
@@ -0,0 +1,4 @@+name: Cabal+version: 1.18.1.3+id: Cabal-1.18.1.3-2b161c6bf77657aa17e1681d83cb051b+exposed: True
+ test/data/cabal-project/Baz.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE QuasiQuotes #-}+module Baz (baz) where+import Foo (fooQ)++baz = [fooQ| foo bar baz |]
+ test/data/cabal-project/Foo.hs view
@@ -0,0 +1,9 @@+module Foo (foo, fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++foo :: ExpQ+foo = stringE "foo"++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/cabal-project/Info.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Info () where++fib :: Int -> Int+fib 0 = 0+fib 1 = 1+fib n = fib (n - 1) + fib (n - 2)
+ test/data/cabal-project/Main.hs view
@@ -0,0 +1,3 @@+import Bar (bar)++main = putStrLn bar
+ test/data/cabal-project/cabal.sandbox.config.in view
@@ -0,0 +1,25 @@+-- This is a Cabal package environment file.+-- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.+-- Please create a 'cabal.config' file in the same directory+-- if you want to change the default settings for this sandbox.+++local-repo: @CWD@/test/data/cabal-project/.cabal-sandbox/packages+logs-dir: @CWD@/test/data/cabal-project/.cabal-sandbox/logs+world-file: @CWD@/test/data/cabal-project/.cabal-sandbox/world+user-install: False+package-db: @CWD@/test/data/cabal-project/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d+build-summary: @CWD@/test/data/cabal-project/.cabal-sandbox/logs/build.log++install-dirs+ prefix: @CWD@/test/data/cabal-project/.cabal-sandbox+ bindir: $prefix/bin+ libdir: $prefix/lib+ libsubdir: $arch-$os-$compiler/$pkgid+ libexecdir: $prefix/libexec+ datadir: $prefix/share+ datasubdir: $arch-$os-$compiler/$pkgid+ docdir: $datadir/doc/$arch-$os-$compiler/$pkgid+ htmldir: $docdir/html+ haddockdir: $htmldir+ sysconfdir: $prefix/etc
+ test/data/cabal-project/cabalapi.cabal view
@@ -0,0 +1,67 @@+Name: ghc-mod+Version: 1.11.3+Author: Kazu Yamamoto <kazu@iij.ad.jp>+Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>+License: BSD3+License-File: LICENSE+Homepage: http://www.mew.org/~kazu/proj/ghc-mod/+Synopsis: Happy Haskell programming on Emacs/Vim+Description: This packages includes Elisp files+ and a Haskell command, "ghc-mod".+ "ghc*.el" enable completion of+ Haskell symbols on Emacs.+ Flymake is also integrated.+ "ghc-mod" is a backend of "ghc*.el".+ It lists up all installed modules+ or extracts names of functions, classes,+ and data declarations.+ To use "ghc-mod" on Vim,+ see <https://github.com/eagletmt/ghcmod-vim> or+ <https://github.com/scrooloose/syntastic>+Category: Development+Cabal-Version: >= 1.6+Build-Type: Simple+Data-Dir: elisp+Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el+ ghc-flymake.el ghc-command.el ghc-info.el+ ghc-ins-mod.el ghc-indent.el+Executable ghc-mod+ Main-Is: GHCMod.hs+ Other-Modules: Browse+ CabalApi+ Cabal+ CabalDev+ Check+ ErrMsg+ Flag+ GHCApi+ GHCChoice+ Gap+ Info+ Lang+ Lint+ List+ Paths_ghc_mod+ Types+ GHC-Options: -Wall+ Build-Depends: base+ , Cabal >= 1.10+ , template-haskell++Test-Suite spec+ Main-Is: Spec.hs+ Hs-Source-Dirs: test, .+ Type: exitcode-stdio-1.0+ Other-Modules: Expectation+ BrowseSpec+ CabalApiSpec+ FlagSpec+ LangSpec+ LintSpec+ ListSpec+ Build-Depends: base+ , Cabal >= 1.10++Source-Repository head+ Type: git+ Location: git://github.com/kazu-yamamoto/ghc-mod.git
+ test/data/cabal-project/subdir1/subdir2/dummy view
@@ -0,0 +1,1 @@+dummy
− test/data/cabal.sandbox.config.in
@@ -1,25 +0,0 @@--- This is a Cabal package environment file.--- THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.--- Please create a 'cabal.config' file in the same directory--- if you want to change the default settings for this sandbox.---local-repo: @CWD@/test/data/.cabal-sandbox/packages-logs-dir: @CWD@/test/data/.cabal-sandbox/logs-world-file: @CWD@/test/data/.cabal-sandbox/world-user-install: False-package-db: @CWD@/test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d-build-summary: @CWD@/test/data/.cabal-sandbox/logs/build.log--install-dirs- prefix: @CWD@/test/data/.cabal-sandbox- bindir: $prefix/bin- libdir: $prefix/lib- libsubdir: $arch-$os-$compiler/$pkgid- libexecdir: $prefix/libexec- datadir: $prefix/share- datasubdir: $arch-$os-$compiler/$pkgid- docdir: $datadir/doc/$arch-$os-$compiler/$pkgid- htmldir: $docdir/html- haddockdir: $htmldir- sysconfdir: $prefix/etc
− test/data/cabalapi.cabal
@@ -1,67 +0,0 @@-Name: ghc-mod-Version: 1.11.3-Author: Kazu Yamamoto <kazu@iij.ad.jp>-Maintainer: Kazu Yamamoto <kazu@iij.ad.jp>-License: BSD3-License-File: LICENSE-Homepage: http://www.mew.org/~kazu/proj/ghc-mod/-Synopsis: Happy Haskell programming on Emacs/Vim-Description: This packages includes Elisp files- and a Haskell command, "ghc-mod".- "ghc*.el" enable completion of- Haskell symbols on Emacs.- Flymake is also integrated.- "ghc-mod" is a backend of "ghc*.el".- It lists up all installed modules- or extracts names of functions, classes,- and data declarations.- To use "ghc-mod" on Vim,- see <https://github.com/eagletmt/ghcmod-vim> or- <https://github.com/scrooloose/syntastic>-Category: Development-Cabal-Version: >= 1.6-Build-Type: Simple-Data-Dir: elisp-Data-Files: Makefile ghc.el ghc-func.el ghc-doc.el ghc-comp.el- ghc-flymake.el ghc-command.el ghc-info.el- ghc-ins-mod.el ghc-indent.el-Executable ghc-mod- Main-Is: GHCMod.hs- Other-Modules: Browse- CabalApi- Cabal- CabalDev- Check- ErrMsg- Flag- GHCApi- GHCChoice- Gap- Info- Lang- Lint- List- Paths_ghc_mod- Types- GHC-Options: -Wall- Build-Depends: base >= 4.0 && < 5- , Cabal >= 1.10- , template-haskell--Test-Suite spec- Main-Is: Spec.hs- Hs-Source-Dirs: test, .- Type: exitcode-stdio-1.0- Other-Modules: Expectation- BrowseSpec- CabalApiSpec- FlagSpec- LangSpec- LintSpec- ListSpec- Build-Depends: base >= 4.0 && < 5- , Cabal >= 1.10--Source-Repository head- Type: git- Location: git://github.com/kazu-yamamoto/ghc-mod.git
+ test/data/check-missing-warnings/DesugarWarnings.hs view
@@ -0,0 +1,5 @@+module Warnings (zoo) where++zoo :: [a] -> ()+zoo x = case x of+ [] -> undefined
test/data/check-test-subdir/check-test-subdir.cabal view
@@ -13,3 +13,4 @@ build-depends: base == 4.* hs-source-dirs: test main-is: Main.hs+ ghc-options: -Wall
+ test/data/custom-cradle/custom-cradle.cabal view
@@ -0,0 +1,12 @@+name: custom-cradle+version: 0.1.0.0+homepage: asd+license-file: LICENSE+author: asd+maintainer: asd+build-type: Simple+cabal-version: >=1.10++library+ build-depends: base+ default-language: Haskell2010
+ test/data/custom-cradle/ghc-mod.package-db-stack view
@@ -0,0 +1,5 @@+global+user+package-db-a+package-db-b+package-db-c
+ test/data/custom-cradle/package-db-a/.gitkeep view
+ test/data/custom-cradle/package-db-b/.gitkeep view
+ test/data/custom-cradle/package-db-c/.gitkeep view
+ test/data/foreign-export/ForeignExport.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE ForeignFunctionInterface #-}++module ForeignExport where++import Foreign.C.Types++foreign export ccall foo :: CUInt++foo :: CUInt+foo = 123
− test/data/ghc-mod-check/Data/Foo.hs
@@ -1,11 +0,0 @@-module Data.Foo where--foo :: Int-foo = undefined--fibonacci :: Int -> Integer-fibonacci n = fib 1 0 1- where- fib m x y- | n == m = y- | otherwise = fib (m+1) y (x + y)
test/data/ghc-mod-check/ghc-mod-check.cabal view
@@ -15,13 +15,12 @@ cabal-version: >=1.8 library- -- exposed-modules:- -- other-modules:+ HS-Source-Dirs: lib build-depends: base exposed-modules: Data.Foo executable foo Main-Is: main.hs GHC-Options: -Wall- Build-Depends: base >= 4 && < 5+ Build-Depends: base , ghc-mod-check
+ test/data/ghc-mod-check/lib/Data/Foo.hs view
@@ -0,0 +1,11 @@+module Data.Foo where++foo :: Int+foo = undefined++fibonacci :: Int -> Integer+fibonacci n = fib 1 0 1+ where+ fib m x y+ | n == m = y+ | otherwise = fib (m+1) y (x + y)
− test/data/hlint.hs
@@ -1,5 +0,0 @@-module Hlist where--main :: IO ()-main = do- putStrLn "Hello, world!"
+ test/data/hlint/hlint.hs view
@@ -0,0 +1,5 @@+module Hlist where++main :: IO ()+main = do+ putStrLn "Hello, world!"
+ test/data/home-module-graph/cpp/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/cpp/A1.hs view
@@ -0,0 +1,4 @@+{-# LANGUAGE CPP #-}+module A1 where+#elif+import B
+ test/data/home-module-graph/cpp/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/cpp/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/cpp/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/cycle/A.hs view
@@ -0,0 +1,2 @@+module A where+import B
+ test/data/home-module-graph/cycle/B.hs view
@@ -0,0 +1,2 @@+module B where+import A
+ test/data/home-module-graph/errors/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/errors/A1.hs view
@@ -0,0 +1,4 @@+module A1 where+psogduapzsü9+import B+lxäö,vLMCks
+ test/data/home-module-graph/errors/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/errors/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/errors/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect-update/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/indirect-update/A1.hs view
@@ -0,0 +1,2 @@+module A1 where+import B
+ test/data/home-module-graph/indirect-update/A2.hs view
@@ -0,0 +1,1 @@+module A2 where
+ test/data/home-module-graph/indirect-update/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/indirect-update/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect-update/C.hs view
@@ -0,0 +1,1 @@+module C where
+ test/data/home-module-graph/indirect/A.hs view
@@ -0,0 +1,4 @@+module A where+import A1+import A2+import A3
+ test/data/home-module-graph/indirect/A1.hs view
@@ -0,0 +1,2 @@+module A1 where+import B
+ test/data/home-module-graph/indirect/A2.hs view
@@ -0,0 +1,2 @@+module A2 where+import C
+ test/data/home-module-graph/indirect/A3.hs view
@@ -0,0 +1,2 @@+module A3 where+import B
+ test/data/home-module-graph/indirect/B.hs view
@@ -0,0 +1,1 @@+module B where
+ test/data/home-module-graph/indirect/C.hs view
@@ -0,0 +1,1 @@+module C where
+ test/data/import-cycle/Mutual1.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Mutual1 where++import Mutual2
+ test/data/import-cycle/Mutual2.hs view
@@ -0,0 +1,3 @@+module Mutual2 where++import Mutual1
+ test/data/non-exported/Fib.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted++module Fib () where++fib :: Int -> Int+fib 0 = 0+fib 1 = 1+fib n = fib (n - 1) + fib (n - 2)
test/data/pattern-synonyms/pattern-synonyms.cabal view
@@ -1,24 +1,25 @@--- Initial pattern-synonyms.cabal generated by cabal init. For further +-- Initial pattern-synonyms.cabal generated by cabal init. For further -- documentation, see http://haskell.org/cabal/users-guide/ name: pattern-synonyms version: 0.1.0.0--- synopsis: --- description: --- license: +-- synopsis:+-- description:+-- license: license-file: LICENSE author: Daniel Gröber maintainer: dxld@darkboxed.org--- copyright: --- category: +-- copyright:+-- category: build-type: Simple--- extra-source-files: +-- extra-source-files: cabal-version: >=1.10 library exposed-modules: A, B- -- other-modules: + -- other-modules: other-extensions: PatternSynonyms- build-depends: base >=4.7 && <4.8- -- hs-source-dirs: + build-depends: base+ -- hs-source-dirs: default-language: Haskell2010+ ghc-options: -Wall
+ test/data/quasi-quotes/FooQ.hs view
@@ -0,0 +1,6 @@+module FooQ (fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/quasi-quotes/QuasiQuotes.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE QuasiQuotes #-}+module QuasiQuotes where++import FooQ++bar = [fooQ| foo bar baz |]
− test/data/subdir1/subdir2/dummy
@@ -1,1 +0,0 @@-dummy
+ test/data/target/Cpp.hs view
@@ -0,0 +1,7 @@+{-# LANGUAGE CPP #-}+#undef NOTHING+#ifdef NOTHING+module WRONG_MODULE where+#else+module Cpp where+#endif
+ test/data/template-haskell/Bar.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-}+module Bar (bar) where+import Foo (foo)++bar = $foo ++ "bar"
+ test/data/template-haskell/Foo.hs view
@@ -0,0 +1,9 @@+module Foo (foo, fooQ) where+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter(..))++foo :: ExpQ+foo = stringE "foo"++fooQ :: QuasiQuoter+fooQ = QuasiQuoter (litE . stringL) undefined undefined undefined
+ test/data/template-haskell/ImportsTH.hs view
@@ -0,0 +1,3 @@+import Bar (bar)++main = putStrLn bar
test/doctests.hs view
@@ -1,12 +1,15 @@+{-# LANGUAGE CPP #-} module Main where import Test.DocTest main :: IO ()-main = doctest [- "-package"- , "ghc"- , "-XConstraintKinds", "-XFlexibleContexts"+main = doctest+ [ "-package", "ghc-" ++ VERSION_ghc+ , "-package", "transformers-" ++ VERSION_transformers+ , "-package", "mtl-" ++ VERSION_mtl+ , "-package", "directory-" ++ VERSION_directory+ , "-XScopedTypeVariables", "-XRecordWildCards", "-XNamedFieldPuns", "-XConstraintKinds", "-XFlexibleContexts", "-XDataKinds", "-XKindSignatures", "-XTypeOperators" , "-idist/build/autogen/" , "-optP-include" , "-optPdist/build/autogen/cabal_macros.h"