From 86a7f954e3dca75df9eb33d6a4359fb49825dd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 27 Jun 2014 19:31:34 +0200 Subject: [PATCH 01/37] Add a comment discouraging the use of `toGhcMod` in new code --- Language/Haskell/GhcMod/Monad.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 7457d72..5df48c4 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -124,6 +124,7 @@ withErrorHandler label = ghandle ignore hPrint stderr e exitSuccess +-- | This is only a transitional mechanism don't use it for new code. toGhcMod :: Ghc a -> GhcMod a toGhcMod a = do s <- gmGhcSession <$> ask From c5776c220b2cf68a497a7479e112161b21b6da84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Jul 2014 19:19:36 +0200 Subject: [PATCH 02/37] Update .travis.yml - Travis-ci now supports testing multiple ghc versions out of the box. - Don't bother running the tests in tree and go straight to testing from the tarball realease --- .travis.yml | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6da67d..e80d0e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,25 @@ -env: - - GHCVER=7.4.2 - - GHCVER=7.6.3 - - GHCVER=7.8.2 - -before_install: - - sudo add-apt-repository -y ppa:hvr/ghc - - sudo apt-get update - - sudo apt-get install cabal-install-1.18 ghc-$GHCVER happy-1.19.3 - - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.18/bin:/opt/happy/1.19.3/bin:$PATH +language: haskell +ghc: + - 7.4 + - 7.6 + - 7.8 install: - cabal update - - cabal install --only-dependencies --enable-tests + - cabal install happy + - happy --version + - cabal install -j --only-dependencies --enable-tests script: + - cabal check + - cabal sdist + - export SRC_TGZ="$PWD/dist/$(cabal info . | awk '{print $2 ".tar.gz";exit}')" + - rm -rf /tmp/test && mkdir -p /tmp/test + - cd /tmp/test + - tar -xf $SRC_TGZ && cd ghc-mod*/ - cabal configure --enable-tests - cabal build - cabal test - - cabal check - - cabal sdist - # The following scriptlet checks that the resulting source distribution can be built & installed - - export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}'); - cd dist/; - if [ -f "$SRC_TGZ" ]; then - cabal install --enable-tests "$SRC_TGZ"; - else - echo "expected '$SRC_TGZ' not found"; - exit 1; - fi matrix: allow_failures: From efb91a8bd9050c5b1a0e767a1c11c4c9363d4acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 4 Jul 2014 19:00:38 +0200 Subject: [PATCH 03/37] Add workaround for #277 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e80d0e7..10905cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ ghc: install: - cabal update - - cabal install happy + - cabal install happy --constraint 'transformers <= 0.3.0.0' - happy --version - cabal install -j --only-dependencies --enable-tests From 648f914ac51313b45d5d075473ef554464701170 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 25 Jun 2014 15:05:23 +0900 Subject: [PATCH 04/37] ghc-debug displays PATH env. --- elisp/ghc.el | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/elisp/ghc.el b/elisp/ghc.el index 5640dad..4b15f6d 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -121,13 +121,14 @@ (defun ghc-debug () (interactive) (let ((el-path (locate-file "ghc.el" load-path)) - (ghc-path (executable-find "ghc")) + (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))) + (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") @@ -139,6 +140,8 @@ (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 (format "\t%s\n" ghc-ver)) + (insert "\nEnvironment variables:\n") + (insert (format "\tPATH=%s\n" path)))) (provide 'ghc) From d68f7b2d186adffe5408415cdbc4a78a1ed82930 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 30 Jun 2014 12:26:30 +0900 Subject: [PATCH 05/37] removing a warning of elisp. --- elisp/ghc-comp.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elisp/ghc-comp.el b/elisp/ghc-comp.el index 0967e62..dd40cf7 100644 --- a/elisp/ghc-comp.el +++ b/elisp/ghc-comp.el @@ -168,7 +168,7 @@ unloaded modules are loaded") (ghc-reset-window-configuration) (ghc-save-window-configuration) (with-output-to-temp-buffer ghc-completion-buffer-name - (display-completion-list list pattern)))))))) + (display-completion-list list)))))))) (defun ghc-save-window-configuration () (unless (get-buffer-window ghc-completion-buffer-name) From d696214816b088906bd65a1f6aa9fcf9be649db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Sun, 18 May 2014 01:20:13 +0000 Subject: [PATCH 06/37] We really don't want mtl < 2.0 (different API) --- ghc-mod.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index ec9b820..14e9d55 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -100,7 +100,7 @@ Library , time , transformers , transformers-base - , mtl + , mtl >= 2.0 , monad-control , split , haskell-src-exts @@ -178,7 +178,7 @@ Test-Suite spec , time , transformers , transformers-base - , mtl + , mtl >= 2.0 , monad-control , hspec >= 1.8.2 , split From dc5ba6d00d4558dfb9379524bf567085a2141e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Sun, 18 May 2014 01:32:09 +0000 Subject: [PATCH 07/37] Add newGhcModEnv for allowing multiple active sessions Conflicts: Language/Haskell/GhcMod/Monad.hs --- Language/Haskell/GhcMod/Cradle.hs | 7 +++++-- Language/Haskell/GhcMod/Monad.hs | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 1e6e594..ecfc1c8 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -1,5 +1,6 @@ module Language.Haskell.GhcMod.Cradle ( findCradle + , findCradle' , findCradleWithoutSandbox ) where @@ -22,8 +23,10 @@ import System.FilePath ((), takeDirectory) -- in a cabal directory. findCradle :: IO Cradle findCradle = do - wdir <- getCurrentDirectory - cabalCradle wdir ||> sandboxCradle wdir ||> plainCradle wdir + findCradle' =<< getCurrentDirectory + +findCradle' :: FilePath -> IO Cradle +findCradle' dir = cabalCradle dir ||> sandboxCradle dir ||> plainCradle dir cabalCradle :: FilePath -> IO Cradle cabalCradle wdir = do diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 5df48c4..34f362a 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -9,6 +9,7 @@ module Language.Haskell.GhcMod.Monad ( , GhcModState(..) , runGhcMod' , runGhcMod + , newGhcModEnv , withErrorHandler , toGhcMod , options @@ -53,6 +54,7 @@ import Control.Monad.Writer.Class import Data.IORef (IORef, readIORef, writeIORef, newIORef) import System.Exit (exitSuccess) import System.IO (hPutStr, hPrint, stderr) +import System.Directory (getCurrentDirectory) ---------------------------------------------------------------- @@ -98,18 +100,23 @@ runGhcMod' r s a = do (a', s',w) <- runRWST (unGhcMod $ initGhcMonad (Just libdir) >> a) r s return (a',(s',w)) +newGhcModEnv :: Options -> FilePath -> IO GhcModEnv +newGhcModEnv opt dir = do + session <- newIORef (error "empty session") + cradle <- findCradle' dir + return GhcModEnv { + gmGhcSession = session + , gmOptions = opt + , gmCradle = cradle + } runGhcMod :: Options -> GhcMod a -> IO a runGhcMod opt action = do - session <- newIORef (error "empty session") - cradle <- findCradle - let env = GhcModEnv { gmGhcSession = session - , gmOptions = opt - , gmCradle = cradle } + env <- liftIO $ newGhcModEnv opt =<< getCurrentDirectory (a,(_,_)) <- runGhcMod' env defaultState $ do dflags <- getSessionDynFlags defaultCleanupHandler dflags $ do - toGhcMod $ initializeFlagsWithCradle opt cradle + toGhcMod $ initializeFlagsWithCradle opt (gmCradle env) action return a From 4b6a687bc10da195fa40ecef65cc70bbe32b56e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 03:10:37 +0200 Subject: [PATCH 08/37] Migrate the remaining parts of the exposed API to `GhcMod a` --- Language/Haskell/GhcMod.hs | 28 ++++++++------- Language/Haskell/GhcMod/Boot.hs | 17 ++-------- Language/Haskell/GhcMod/CaseSplit.hs | 18 ++-------- Language/Haskell/GhcMod/Debug.hs | 49 +++++++++++--------------- Language/Haskell/GhcMod/FillSig.hs | 22 +++--------- Language/Haskell/GhcMod/Flag.hs | 12 +++---- Language/Haskell/GhcMod/GHCApi.hs | 19 +++++------ Language/Haskell/GhcMod/Ghc.hs | 16 --------- Language/Haskell/GhcMod/Info.hs | 27 +-------------- Language/Haskell/GhcMod/Internal.hs | 3 +- Language/Haskell/GhcMod/Lang.hs | 6 ++-- Language/Haskell/GhcMod/Lint.hs | 20 ++++++----- Language/Haskell/GhcMod/List.hs | 6 +--- Language/Haskell/GhcMod/Monad.hs | 8 +++-- Language/Haskell/GhcMod/PkgDoc.hs | 26 ++++++-------- ghc-mod.cabal | 1 + src/GHCMod.hs | 39 ++++++++++----------- src/GHCModi.hs | 51 ++++++++++++---------------- test/BrowseSpec.hs | 14 ++++---- test/CheckSpec.hs | 12 +++---- test/FlagSpec.hs | 9 ++--- test/GhcPkgSpec.hs | 4 +-- test/InfoSpec.hs | 18 +++++----- test/LangSpec.hs | 7 ++-- test/LintSpec.hs | 13 +++---- test/ListSpec.hs | 8 ++--- test/Main.hs | 3 +- 27 files changed, 180 insertions(+), 276 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index 4f081db..01c0e04 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -12,23 +12,25 @@ module Language.Haskell.GhcMod ( -- * Types , ModuleString , Expression - -- * 'IO' utilities - , bootInfo + , GhcPkgDb + -- * 'GhcMod' utilities + , boot , browse + , check , checkSyntax - , lintSyntax - , expandTemplate - , infoExpr - , typeExpr - , fillSig - , listModules - , listLanguages - , listFlags , debugInfo - , rootInfo - , packageDoc + , expandTemplate , findSymbol - , splitVar + , info + , lint + , pkgDoc + , rootInfo + , types + , splits + , sig + , modules + , languages + , flags ) where import Language.Haskell.GhcMod.Boot diff --git a/Language/Haskell/GhcMod/Boot.hs b/Language/Haskell/GhcMod/Boot.hs index 8cdfeb1..fd9345c 100644 --- a/Language/Haskell/GhcMod/Boot.hs +++ b/Language/Haskell/GhcMod/Boot.hs @@ -1,27 +1,16 @@ module Language.Haskell.GhcMod.Boot where -import Control.Applicative ((<$>)) -import CoreMonad (liftIO, liftIO) +import Control.Applicative import Language.Haskell.GhcMod.Browse import Language.Haskell.GhcMod.Flag import Language.Haskell.GhcMod.Lang import Language.Haskell.GhcMod.List import Language.Haskell.GhcMod.Monad -import Language.Haskell.GhcMod.Types - --- | Printing necessary information for front-end booting. -bootInfo :: Options -> IO String -bootInfo opt = runGhcMod opt $ boot -- | Printing necessary information for front-end booting. boot :: GhcMod String -boot = do - opt <- options - mods <- modules - langs <- liftIO $ listLanguages opt - flags <- liftIO $ listFlags opt - pre <- concat <$> mapM browse preBrowsedModules - return $ mods ++ langs ++ flags ++ pre +boot = concat <$> sequence [modules, languages, flags, + concat <$> mapM browse preBrowsedModules] preBrowsedModules :: [String] preBrowsedModules = [ diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 3399a3c..5067d5e 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -1,8 +1,7 @@ {-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.CaseSplit ( - splitVar - , splits + splits ) where import Data.List (find, intercalate) @@ -11,12 +10,10 @@ import qualified Data.Text.IO as T (readFile) import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, LHsBind, LHsExpr, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G -import Language.Haskell.GhcMod.GHCApi import Language.Haskell.GhcMod.Gap (HasType(..)) import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils -import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Convert import MonadUtils (liftIO) import Outputable (PprStyle) @@ -35,17 +32,6 @@ data SplitToTextInfo = SplitToTextInfo { sVarName :: String , sTycons :: [String] } --- | Splitting a variable in a equation. -splitVar :: Options - -> Cradle - -> FilePath -- ^ A target file. - -> Int -- ^ Line number. - -> Int -- ^ Column number. - -> IO String -splitVar opt cradle file lineNo colNo = runGhcMod opt $ do - initializeFlagsWithCradle opt cradle - splits file lineNo colNo - -- | Splitting a variable in a equation. splits :: FilePath -- ^ A target file. -> Int -- ^ Line number. @@ -212,7 +198,7 @@ srcSpanDifference b v = 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 text (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' diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 8de00dc..b3ce715 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -1,55 +1,44 @@ module Language.Haskell.GhcMod.Debug (debugInfo, rootInfo) where import Control.Applicative ((<$>)) -import Control.Exception.IOChoice ((||>)) import CoreMonad (liftIO) import Data.List (intercalate) -import Data.Maybe (fromMaybe, isJust, fromJust) +import Data.Maybe (isJust, fromJust) import Language.Haskell.GhcMod.CabalApi import Language.Haskell.GhcMod.GHCApi +import Language.Haskell.GhcMod.GHCChoice ((||>)) import Language.Haskell.GhcMod.Convert +import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types ---------------------------------------------------------------- -- | Obtaining debug information. -debugInfo :: Options - -> Cradle - -> IO String -debugInfo opt cradle = convert opt <$> do +debugInfo :: GhcMod String +debugInfo = cradle >>= \c -> convert' =<< do CompilerOptions gopts incDir pkgs <- - if cabal then - liftIO (fromCabalFile ||> return simpleCompilerOption) + if isJust $ cradleCabalFile c then + (fromCabalFile c ||> simpleCompilerOption) else - return simpleCompilerOption - mglibdir <- liftIO getSystemLibDir + simpleCompilerOption return [ - "Root directory: " ++ rootDir - , "Current directory: " ++ currentDir - , "Cabal file: " ++ cabalFile + "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: " ++ fromMaybe "" mglibdir + , "System libraries: " ++ systemLibDir ] where - currentDir = cradleCurrentDir cradle - mCabalFile = cradleCabalFile cradle - rootDir = cradleRootDir cradle - cabal = isJust mCabalFile - cabalFile = fromMaybe "" mCabalFile - origGopts = ghcOpts opt - simpleCompilerOption = CompilerOptions origGopts [] [] - fromCabalFile = do - pkgDesc <- parseCabalFile file - getCompilerOptions origGopts cradle pkgDesc - where - file = fromJust mCabalFile + simpleCompilerOption = options >>= \op -> + return $ CompilerOptions (ghcOpts op) [] [] + fromCabalFile c = options >>= \opts -> liftIO $ do + pkgDesc <- parseCabalFile $ fromJust $ cradleCabalFile c + getCompilerOptions (ghcOpts opts) c pkgDesc ---------------------------------------------------------------- -- | Obtaining root information. -rootInfo :: Options - -> Cradle - -> IO String -rootInfo opt cradle = return $ convert opt $ cradleRootDir cradle +rootInfo :: GhcMod String +rootInfo = convert' =<< cradleRootDir <$> cradle diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index 8e3566f..88a83c7 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -1,8 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, CPP #-} module Language.Haskell.GhcMod.FillSig ( - fillSig - , sig + sig ) where import Data.Char (isSymbol) @@ -10,12 +9,10 @@ import Data.List (find, intercalate) import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G -import Language.Haskell.GhcMod.GHCApi import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils -import Language.Haskell.GhcMod.Types import MonadUtils (liftIO) import Outputable (PprStyle) import qualified Type as Ty @@ -38,18 +35,7 @@ data SigInfo = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName) | InstanceDecl SrcSpan G.Class -- Signature for fallback operation via haskell-src-exts -data HESigInfo = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo) - --- | Create a initial body from a signature. -fillSig :: Options - -> Cradle - -> FilePath -- ^ A target file. - -> Int -- ^ Line number. - -> Int -- ^ Column number. - -> IO String -fillSig opt cradle file lineNo colNo = runGhcMod opt $ do - initializeFlagsWithCradle opt cradle - sig file lineNo colNo +data HESigInfo = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo) -- | Create a initial body from a signature. sig :: FilePath -- ^ A target file. @@ -67,13 +53,13 @@ sig file lineNo colNo = ghandle handler body InstanceDecl loc cls -> do ("instance", fourInts loc, map (\x -> initialBody dflag style (G.idType x) x) (Ty.classMethods cls)) - + handler (SomeException _) = do opt <- options -- Code cannot be parsed by ghc module -- Fallback: try to get information via haskell-src-exts whenFound opt (getSignatureFromHE file lineNo colNo) $ - \(HESignature loc names ty) -> + \(HESignature loc names ty) -> ("function", fourIntsHE loc, map (initialBody undefined undefined ty) names) ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/Flag.hs b/Language/Haskell/GhcMod/Flag.hs index ff00fde..74319e8 100644 --- a/Language/Haskell/GhcMod/Flag.hs +++ b/Language/Haskell/GhcMod/Flag.hs @@ -2,12 +2,12 @@ module Language.Haskell.GhcMod.Flag where import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Convert -import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad -- | Listing GHC flags. (e.g -fno-warn-orphans) -listFlags :: Options -> IO String -listFlags opt = return $ convert opt [ "-f" ++ prefix ++ option - | option <- Gap.fOptions - , prefix <- ["","no-"] - ] +flags :: GhcMod String +flags = convert' [ "-f" ++ prefix ++ option + | option <- Gap.fOptions + , prefix <- ["","no-"] + ] diff --git a/Language/Haskell/GhcMod/GHCApi.hs b/Language/Haskell/GhcMod/GHCApi.hs index d48b2e7..2cd0886 100644 --- a/Language/Haskell/GhcMod/GHCApi.hs +++ b/Language/Haskell/GhcMod/GHCApi.hs @@ -6,7 +6,7 @@ module Language.Haskell.GhcMod.GHCApi ( , initializeFlagsWithCradle , setTargetFiles , getDynamicFlags - , getSystemLibDir + , systemLibDir , withDynFlags , withCmdFlags , setNoWaringFlags @@ -16,6 +16,8 @@ module Language.Haskell.GhcMod.GHCApi ( import Language.Haskell.GhcMod.CabalApi import Language.Haskell.GhcMod.GHCChoice import Language.Haskell.GhcMod.GhcPkg +import qualified Language.Haskell.GhcMod.Gap as Gap +import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) import Control.Monad (forM, void) @@ -25,8 +27,6 @@ import GHC (DynFlags(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) import qualified GHC as G import GhcMonad import GHC.Paths (libdir) -import qualified Language.Haskell.GhcMod.Gap as Gap -import Language.Haskell.GhcMod.Types import System.Exit (exitSuccess) import System.IO (hPutStr, hPrint, stderr) import System.IO.Unsafe (unsafePerformIO) @@ -34,8 +34,8 @@ import System.IO.Unsafe (unsafePerformIO) ---------------------------------------------------------------- -- | Obtaining the directory for system libraries. -getSystemLibDir :: IO (Maybe FilePath) -getSystemLibDir = return $ Just libdir +systemLibDir :: FilePath +systemLibDir = libdir ---------------------------------------------------------------- @@ -53,8 +53,7 @@ withGHC file body = ghandle ignore $ withGHC' body withGHC' :: Ghc a -> IO a withGHC' body = do - mlibdir <- getSystemLibDir - G.runGhc mlibdir $ do + G.runGhc (Just systemLibDir) $ do dflags <- G.getSessionDynFlags G.defaultCleanupHandler dflags body @@ -161,8 +160,7 @@ setTargetFiles files = do -- | Return the 'DynFlags' currently in use in the GHC session. getDynamicFlags :: IO DynFlags getDynamicFlags = do - mlibdir <- getSystemLibDir - G.runGhc mlibdir G.getSessionDynFlags + G.runGhc (Just systemLibDir) G.getSessionDynFlags withDynFlags :: GhcMonad m => (DynFlags -> DynFlags) @@ -197,8 +195,7 @@ setAllWaringFlags df = df { warningFlags = allWarningFlags } allWarningFlags :: Gap.WarnFlags allWarningFlags = unsafePerformIO $ do - mlibdir <- getSystemLibDir - G.runGhc mlibdir $ do + G.runGhc (Just systemLibDir) $ do df <- G.getSessionDynFlags df' <- addCmdOpts ["-Wall"] df return $ G.warningFlags df' diff --git a/Language/Haskell/GhcMod/Ghc.hs b/Language/Haskell/GhcMod/Ghc.hs index 112dfd1..b2259db 100644 --- a/Language/Haskell/GhcMod/Ghc.hs +++ b/Language/Haskell/GhcMod/Ghc.hs @@ -2,15 +2,6 @@ module Language.Haskell.GhcMod.Ghc ( -- * Converting the 'Ghc' monad to the 'IO' monad withGHC , withGHC' - -- * 'Ghc' utilities - , boot - , browse - , check - , info - , types - , splits - , sig - , modules -- * 'SymMdlDb' , Symbol , SymMdlDb @@ -19,12 +10,5 @@ module Language.Haskell.GhcMod.Ghc ( , lookupSym' ) where -import Language.Haskell.GhcMod.Boot -import Language.Haskell.GhcMod.Browse -import Language.Haskell.GhcMod.Check import Language.Haskell.GhcMod.Find import Language.Haskell.GhcMod.GHCApi -import Language.Haskell.GhcMod.Info -import Language.Haskell.GhcMod.List -import Language.Haskell.GhcMod.FillSig -import Language.Haskell.GhcMod.CaseSplit diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index 4394f9f..8b4afec 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -1,7 +1,5 @@ module Language.Haskell.GhcMod.Info ( - infoExpr - , info - , typeExpr + info , types ) where @@ -13,7 +11,6 @@ import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, LHsBind, LHsExpr, LPat, Id, TypecheckedModule(..), SrcSpan, Type) import qualified GHC as G import Language.Haskell.GhcMod.Doc (showPage) -import Language.Haskell.GhcMod.GHCApi import Language.Haskell.GhcMod.Gap (HasType(..)) import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad @@ -23,16 +20,6 @@ import Language.Haskell.GhcMod.Convert ---------------------------------------------------------------- --- | Obtaining information of a target expression. (GHCi's info:) -infoExpr :: Options - -> Cradle - -> FilePath -- ^ A target file. - -> Expression -- ^ A Haskell expression. - -> IO String -infoExpr opt cradle file expr = runGhcMod opt $ do - initializeFlagsWithCradle opt cradle - info file expr - -- | Obtaining information of a target expression. (GHCi's info:) info :: FilePath -- ^ A target file. -> Expression -- ^ A Haskell expression. @@ -48,17 +35,6 @@ info file expr = do ---------------------------------------------------------------- --- | Obtaining type of a target expression. (GHCi's type:) -typeExpr :: Options - -> Cradle - -> FilePath -- ^ A target file. - -> Int -- ^ Line number. - -> Int -- ^ Column number. - -> IO String -typeExpr opt cradle file lineNo colNo = runGhcMod opt $ do - initializeFlagsWithCradle opt cradle - types file lineNo colNo - -- | Obtaining type of a target expression. (GHCi's type:) types :: FilePath -- ^ A target file. -> Int -- ^ Line number. @@ -85,4 +61,3 @@ getSrcSpanType modSum lineNo colNo = do ets <- mapM (getType tcm) es pts <- mapM (getType tcm) ps return $ catMaybes $ concat [ets, bts, pts] - diff --git a/Language/Haskell/GhcMod/Internal.hs b/Language/Haskell/GhcMod/Internal.hs index 2c5e910..99566e6 100644 --- a/Language/Haskell/GhcMod/Internal.hs +++ b/Language/Haskell/GhcMod/Internal.hs @@ -16,8 +16,9 @@ module Language.Haskell.GhcMod.Internal ( , cabalDependPackages , cabalSourceDirs , cabalAllTargets + -- * GHC.Paths + , systemLibDir -- * IO - , getSystemLibDir , getDynamicFlags -- * Initializing 'DynFlags' , initializeFlagsWithCradle diff --git a/Language/Haskell/GhcMod/Lang.hs b/Language/Haskell/GhcMod/Lang.hs index 1ddc59a..071e178 100644 --- a/Language/Haskell/GhcMod/Lang.hs +++ b/Language/Haskell/GhcMod/Lang.hs @@ -1,10 +1,10 @@ module Language.Haskell.GhcMod.Lang where import DynFlags (supportedLanguagesAndExtensions) -import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Convert +import Language.Haskell.GhcMod.Monad -- | Listing language extensions. -listLanguages :: Options -> IO String -listLanguages opt = return $ convert opt supportedLanguagesAndExtensions +languages :: GhcMod String +languages = convert' supportedLanguagesAndExtensions diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index 23515be..b88ca5f 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -1,19 +1,21 @@ module Language.Haskell.GhcMod.Lint where -import Control.Applicative ((<$>)) -import Control.Exception (handle, SomeException(..)) +import Exception (ghandle) +import Control.Exception (SomeException(..)) +import Control.Monad.Trans (liftIO) import Language.Haskell.GhcMod.Logger (checkErrorPrefix) import Language.Haskell.GhcMod.Convert +import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Language.Haskell.HLint (hlint) -- | Checking syntax of a target file using hlint. -- Warnings and errors are returned. -lintSyntax :: Options - -> FilePath -- ^ A target file. - -> IO String -lintSyntax opt file = handle handler $ pack <$> hlint (file : "--quiet" : hopts) - where - pack = convert opt . map (init . show) -- init drops the last \n. - hopts = hlintOpts opt +lint :: FilePath -- ^ A target file. + -> GhcMod String +lint file = do + opt <- options + ghandle handler . pack =<< (liftIO $ hlint $ file : "--quiet" : hlintOpts opt) + where + pack = convert' . map (init . show) -- init drops the last \n. handler (SomeException e) = return $ checkErrorPrefix ++ show e ++ "\n" diff --git a/Language/Haskell/GhcMod/List.hs b/Language/Haskell/GhcMod/List.hs index 5fcf32a..be0b4c7 100644 --- a/Language/Haskell/GhcMod/List.hs +++ b/Language/Haskell/GhcMod/List.hs @@ -1,4 +1,4 @@ -module Language.Haskell.GhcMod.List (listModules, modules) where +module Language.Haskell.GhcMod.List (modules) where import Control.Applicative ((<$>)) import Control.Exception (SomeException(..)) @@ -12,10 +12,6 @@ import UniqFM (eltsUFM) ---------------------------------------------------------------- --- | Listing installed modules. -listModules :: Options -> Cradle -> IO String -listModules opt _ = runGhcMod opt $ modules - -- | Listing installed modules. modules :: GhcMod String modules = do diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 34f362a..5204130 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -13,6 +13,7 @@ module Language.Haskell.GhcMod.Monad ( , withErrorHandler , toGhcMod , options + , cradle , module Control.Monad.Reader.Class , module Control.Monad.Writer.Class , module Control.Monad.State.Class @@ -103,11 +104,11 @@ runGhcMod' r s a = do newGhcModEnv :: Options -> FilePath -> IO GhcModEnv newGhcModEnv opt dir = do session <- newIORef (error "empty session") - cradle <- findCradle' dir + c <- findCradle' dir return GhcModEnv { gmGhcSession = session , gmOptions = opt - , gmCradle = cradle + , gmCradle = c } runGhcMod :: Options -> GhcMod a -> IO a @@ -142,6 +143,9 @@ toGhcMod a = do options :: GhcMod Options options = gmOptions <$> ask +cradle :: GhcMod Cradle +cradle = gmCradle <$> ask + instance MonadBase IO GhcMod where liftBase = GhcMod . liftBase diff --git a/Language/Haskell/GhcMod/PkgDoc.hs b/Language/Haskell/GhcMod/PkgDoc.hs index f8dd9d4..2f829ab 100644 --- a/Language/Haskell/GhcMod/PkgDoc.hs +++ b/Language/Haskell/GhcMod/PkgDoc.hs @@ -1,30 +1,26 @@ -module Language.Haskell.GhcMod.PkgDoc (packageDoc) where +module Language.Haskell.GhcMod.PkgDoc (pkgDoc) where +import Control.Monad.Trans (liftIO) import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.GhcPkg +import Language.Haskell.GhcMod.Monad import Control.Applicative ((<$>)) import System.Process (readProcess) -- | Obtaining the package name and the doc path of a module. -packageDoc :: Options - -> Cradle - -> ModuleString - -> IO String -packageDoc _ cradle mdl = pkgDoc cradle mdl - -pkgDoc :: Cradle -> String -> IO String -pkgDoc cradle mdl = do - pkg <- trim <$> readProcess "ghc-pkg" toModuleOpts [] +pkgDoc :: String -> GhcMod String +pkgDoc mdl = cradle >>= \c -> liftIO $ do + pkg <- trim <$> readProcess "ghc-pkg" (toModuleOpts c) [] if pkg == "" then return "\n" else do - htmlpath <- readProcess "ghc-pkg" (toDocDirOpts pkg) [] + htmlpath <- readProcess "ghc-pkg" (toDocDirOpts pkg c) [] let ret = pkg ++ " " ++ drop 14 htmlpath return ret where - toModuleOpts = ["find-module", mdl, "--simple-output"] - ++ ghcPkgDbStackOpts (cradlePkgDbStack cradle) - toDocDirOpts pkg = ["field", pkg, "haddock-html"] - ++ ghcPkgDbStackOpts (cradlePkgDbStack cradle) + toModuleOpts c = ["find-module", mdl, "--simple-output"] + ++ ghcPkgDbStackOpts (cradlePkgDbStack c) + toDocDirOpts pkg c = ["field", pkg, "haddock-html"] + ++ ghcPkgDbStackOpts (cradlePkgDbStack c) trim = takeWhile (`notElem` " \n") diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 14e9d55..1cfb162 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -120,6 +120,7 @@ Executable ghc-mod Build-Depends: base >= 4.0 && < 5 , directory , filepath + , mtl >= 2.0 , ghc , ghc-mod diff --git a/src/GHCMod.hs b/src/GHCMod.hs index a2958e6..e2e2a4f 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -5,6 +5,7 @@ module Main where import Config (cProjectVersion) import Control.Applicative ((<$>)) import Control.Exception (Exception, Handler(..), ErrorCall(..)) +import Control.Monad.Trans (liftIO) import qualified Control.Exception as E import Data.Typeable (Typeable) import Data.Version (showVersion) @@ -102,7 +103,6 @@ main = flip E.catches handlers $ do -- #endif args <- getArgs let (opt,cmdArg) = parseArgs argspec args - cradle <- findCradle let cmdArg0 = cmdArg !. 0 cmdArg1 = cmdArg !. 1 cmdArg3 = cmdArg !. 3 @@ -111,23 +111,23 @@ main = flip E.catches handlers $ do nArgs n f = if length remainingArgs == n then f else E.throw (ArgumentsMismatch cmdArg0) - res <- case cmdArg0 of - "list" -> listModules opt cradle - "lang" -> listLanguages opt - "flag" -> listFlags opt - "browse" -> runGhcMod opt $ concat <$> mapM browse remainingArgs - "check" -> runGhcMod opt $ checkSyntax remainingArgs - "expand" -> runGhcMod opt $ expandTemplate remainingArgs - "debug" -> debugInfo opt cradle - "info" -> nArgs 3 infoExpr opt cradle cmdArg1 cmdArg3 - "type" -> nArgs 4 $ typeExpr opt cradle cmdArg1 (read cmdArg3) (read cmdArg4) - "split" -> nArgs 4 $ splitVar opt cradle cmdArg1 (read cmdArg3) (read cmdArg4) - "sig" -> nArgs 4 $ fillSig opt cradle cmdArg1 (read cmdArg3) (read cmdArg4) - "find" -> runGhcMod opt $ nArgs 1 $ findSymbol cmdArg1 - "lint" -> nArgs 1 withFile (lintSyntax opt) cmdArg1 - "root" -> rootInfo opt cradle - "doc" -> nArgs 1 $ packageDoc opt cradle cmdArg1 - "boot" -> bootInfo opt + res <- runGhcMod opt $ case cmdArg0 of + "list" -> modules + "lang" -> languages + "flag" -> flags + "browse" -> concat <$> mapM browse remainingArgs + "check" -> checkSyntax remainingArgs + "expand" -> expandTemplate remainingArgs + "debug" -> debugInfo + "info" -> nArgs 3 info cmdArg1 cmdArg3 + "type" -> nArgs 4 $ types cmdArg1 (read cmdArg3) (read cmdArg4) + "split" -> nArgs 4 $ splits cmdArg1 (read cmdArg3) (read cmdArg4) + "sig" -> nArgs 4 $ sig cmdArg1 (read cmdArg3) (read cmdArg4) + "find" -> nArgs 1 $ findSymbol cmdArg1 + "lint" -> nArgs 1 $ withFile lint cmdArg1 + "root" -> rootInfo + "doc" -> nArgs 1 $ pkgDoc cmdArg1 + "boot" -> boot "version" -> return progVersion "help" -> return $ O.usageInfo usage argspec cmd -> E.throw (NoSuchCommand cmd) @@ -152,8 +152,9 @@ main = flip E.catches handlers $ do hPutStrLn stderr $ "\"" ++ file ++ "\" not found" printUsage printUsage = hPutStrLn stderr $ '\n' : O.usageInfo usage argspec + withFile :: (FilePath -> GhcMod a) -> FilePath -> GhcMod a withFile cmd file = do - exist <- doesFileExist file + exist <- liftIO $ doesFileExist file if exist then cmd file else E.throw (FileNotExist file) diff --git a/src/GHCModi.hs b/src/GHCModi.hs index 0baf43c..39449b4 100644 --- a/src/GHCModi.hs +++ b/src/GHCModi.hs @@ -31,12 +31,12 @@ import Data.Set (Set) import qualified Data.Set as S import Data.Typeable (Typeable) import Data.Version (showVersion) +import Exception (ghandle) import GHC (GhcMonad) import qualified GHC as G import Language.Haskell.GhcMod import Language.Haskell.GhcMod.Ghc import Language.Haskell.GhcMod.Monad -import Language.Haskell.GhcMod.Internal import Paths_ghc_mod import System.Console.GetOpt import System.Directory (setCurrentDirectory) @@ -98,12 +98,11 @@ main = E.handle cmdHandler $ go (opt,_) = E.handle someHandler $ do cradle0 <- findCradle let rootdir = cradleRootDir cradle0 - cradle = cradle0 { cradleCurrentDir = rootdir } +-- c = cradle0 { cradleCurrentDir = rootdir } TODO: ????? setCurrentDirectory rootdir mvar <- liftIO newEmptyMVar - mlibdir <- getSystemLibDir - void $ forkIO $ setupDB cradle mlibdir opt mvar - run cradle mlibdir opt $ loop opt S.empty mvar + void $ forkIO $ runGhcMod opt $ setupDB mvar + runGhcMod opt $ loop S.empty mvar where -- this is just in case. -- If an error is caught here, it is a bug of GhcMod library. @@ -117,31 +116,23 @@ replace (x:xs) = x : replace xs ---------------------------------------------------------------- -run :: Cradle -> Maybe FilePath -> Options -> GhcMod a -> IO a -run _ _ opt body = runGhcMod opt $ do - dflags <- G.getSessionDynFlags - G.defaultCleanupHandler dflags body - ----------------------------------------------------------------- - -setupDB :: Cradle -> Maybe FilePath -> Options -> MVar SymMdlDb -> IO () -setupDB cradle mlibdir opt mvar = E.handle handler $ do - db <- run cradle mlibdir opt getSymMdlDb - putMVar mvar db +setupDB :: MVar SymMdlDb -> GhcMod () +setupDB mvar = ghandle handler $ do + liftIO . putMVar mvar =<< getSymMdlDb where handler (SomeException _) = return () -- fixme: put emptyDb? ---------------------------------------------------------------- -loop :: Options -> Set FilePath -> MVar SymMdlDb -> GhcMod () -loop opt set mvar = do +loop :: Set FilePath -> MVar SymMdlDb -> GhcMod () +loop set mvar = do cmdArg <- liftIO getLine let (cmd,arg') = break (== ' ') cmdArg arg = dropWhile (== ' ') arg' (ret,ok,set') <- case cmd of - "check" -> checkStx opt set arg + "check" -> checkStx set arg "find" -> findSym set arg mvar - "lint" -> toGhcMod $ lintStx opt set arg + "lint" -> lintStx set arg "info" -> showInfo set arg "type" -> showType set arg "split" -> doSplit set arg @@ -157,15 +148,14 @@ loop opt set mvar = do else do liftIO $ putStrLn $ "NG " ++ replace ret liftIO $ hFlush stdout - when ok $ loop opt set' mvar + when ok $ loop set' mvar ---------------------------------------------------------------- -checkStx :: Options - -> Set FilePath +checkStx :: Set FilePath -> FilePath -> GhcMod (String, Bool, Set FilePath) -checkStx _ set file = do +checkStx set file = do set' <- toGhcMod $ newFileSet set file let files = S.toList set' eret <- check files @@ -209,16 +199,17 @@ findSym set sym mvar = do let ret = lookupSym' opt sym db return (ret, True, set) -lintStx :: GhcMonad m - => Options -> Set FilePath -> FilePath - -> m (String, Bool, Set FilePath) -lintStx opt set optFile = liftIO $ do - ret <-lintSyntax opt' file +lintStx :: Set FilePath + -> FilePath + -> GhcMod (String, Bool, Set FilePath) +lintStx set optFile = do + ret <- local env' $ lint file return (ret, True, set) where (opts,file) = parseLintOptions optFile hopts = if opts == "" then [] else read opts - opt' = opt { hlintOpts = hopts } + env' e = e { gmOptions = opt' $ gmOptions e } + opt' o = o { hlintOpts = hopts } -- | -- >>> parseLintOptions "[\"--ignore=Use camelCase\", \"--ignore=Eta reduce\"] file name" diff --git a/test/BrowseSpec.hs b/test/BrowseSpec.hs index 9a0d87d..6ead51f 100644 --- a/test/BrowseSpec.hs +++ b/test/BrowseSpec.hs @@ -10,25 +10,25 @@ import Dir spec :: Spec spec = do - describe "browse" $ do - it "lists up symbols in the module" $ do + describe "browse Data.Map" $ do + it "contains at least `differenceWithKey'" $ do syms <- runD $ lines <$> browse "Data.Map" syms `shouldContain` ["differenceWithKey"] - describe "browse -d" $ do - it "lists up symbols with type info in the module" $ do + describe "browse -d Data.Either" $ do + it "contains functions (e.g. `either') including their type signature" $ do syms <- run defaultOptions { detailed = True } $ lines <$> browse "Data.Either" syms `shouldContain` ["either :: (a -> c) -> (b -> c) -> Either a b -> c"] - it "lists up data constructors with type info in the module" $ do + it "contains type constructors (e.g. `Left') including their type signature" $ do cradle <- findCradle syms <- run defaultOptions { detailed = True} $ lines <$> browse "Data.Either" syms `shouldContain` ["Left :: a -> Either a b"] - describe "browse local" $ do - it "lists symbols in a local module" $ do + 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"] diff --git a/test/CheckSpec.hs b/test/CheckSpec.hs index 71709c8..8b9334e 100644 --- a/test/CheckSpec.hs +++ b/test/CheckSpec.hs @@ -12,28 +12,28 @@ import Dir spec :: Spec spec = do describe "checkSyntax" $ do - it "can check even if an executable depends on its library" $ 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 `shouldBe` "main.hs:5:1:Warning: Top-level binding with no type signature: main :: IO ()\n" - it "can check even if a test module imports another test module located at different directory" $ do + 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"] res `shouldSatisfy` (("test" "Foo.hs:3:1:Warning: Top-level binding with no type signature: foo :: [Char]\n") `isSuffixOf`) - it "can detect mutually imported modules" $ do + it "detects cyclic imports" $ do withDirectory_ "test/data" $ do res <- runID $ checkSyntax ["Mutual1.hs"] res `shouldSatisfy` ("Module imports form a cycle" `isInfixOf`) - it "can check a module using QuasiQuotes" $ do + it "works with modules using QuasiQuotes" $ do withDirectory_ "test/data" $ do res <- runID $ checkSyntax ["Baz.hs"] res `shouldSatisfy` ("Baz.hs:5:1:Warning:" `isPrefixOf`) - context "without errors" $ do - it "doesn't output empty line" $ do + 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"] res `shouldBe` "" diff --git a/test/FlagSpec.hs b/test/FlagSpec.hs index bfdf9ff..80fc893 100644 --- a/test/FlagSpec.hs +++ b/test/FlagSpec.hs @@ -3,10 +3,11 @@ module FlagSpec where import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec +import TestUtils spec :: Spec spec = do - describe "listFlags" $ do - it "lists up GHC flags" $ do - flags <- lines <$> listFlags defaultOptions - flags `shouldContain` ["-fno-warn-orphans"] + describe "flags" $ do + it "contains at least `-fno-warn-orphans'" $ do + f <- runD $ lines <$> flags + f `shouldContain` ["-fno-warn-orphans"] diff --git a/test/GhcPkgSpec.hs b/test/GhcPkgSpec.hs index af39235..8560715 100644 --- a/test/GhcPkgSpec.hs +++ b/test/GhcPkgSpec.hs @@ -18,10 +18,10 @@ spec = do getPackageDbStack "test/data/" `shouldReturn` [GlobalDb, PackageDb $ cwd "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d"] #endif - it "parses a config file and extracts sandbox package db" $ do + it "can parse a config file and extract the sandbox package-db" $ do cwd <- getCurrentDirectory pkgDb <- getSandboxDb "test/data/" pkgDb `shouldBe` (cwd "test/data/.cabal-sandbox/i386-osx-ghc-7.6.3-packages.conf.d") - it "throws an error if a config file is broken" $ do + it "throws an error if the sandbox config file is broken" $ do getSandboxDb "test/data/broken-sandbox" `shouldThrow` anyException diff --git a/test/InfoSpec.hs b/test/InfoSpec.hs index 4ff7809..caba4da 100644 --- a/test/InfoSpec.hs +++ b/test/InfoSpec.hs @@ -14,47 +14,47 @@ import System.Exit import System.FilePath import System.Process import Test.Hspec - +import TestUtils import Dir spec :: Spec spec = do - describe "typeExpr" $ do + describe "types" $ do it "shows types of the expression and its outers" $ do withDirectory_ "test/data/ghc-mod-check" $ do cradle <- findCradleWithoutSandbox - res <- typeExpr defaultOptions cradle "Data/Foo.hs" 9 5 + 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" it "works with a module using TemplateHaskell" $ do withDirectory_ "test/data" $ do cradle <- findCradleWithoutSandbox - res <- typeExpr defaultOptions cradle "Bar.hs" 5 1 + res <- runD $ 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 cradle <- findCradleWithoutSandbox - res <- typeExpr defaultOptions cradle "Main.hs" 3 8 + 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 ()\""] - describe "infoExpr" $ do + describe "info" $ do it "works for non-export functions" $ do withDirectory_ "test/data" $ do cradle <- findCradleWithoutSandbox - res <- infoExpr defaultOptions cradle "Info.hs" "fib" + res <- runD $ info "Info.hs" "fib" res `shouldSatisfy` ("fib :: Int -> Int" `isPrefixOf`) it "works with a module using TemplateHaskell" $ do withDirectory_ "test/data" $ do cradle <- findCradleWithoutSandbox - res <- infoExpr defaultOptions cradle "Bar.hs" "foo" + res <- runD $ info "Bar.hs" "foo" res `shouldSatisfy` ("foo :: ExpQ" `isPrefixOf`) it "works with a module that imports another module using TemplateHaskell" $ do withDirectory_ "test/data" $ do cradle <- findCradleWithoutSandbox - res <- infoExpr defaultOptions cradle "Main.hs" "bar" + res <- runD $ info "Main.hs" "bar" res `shouldSatisfy` ("bar :: [Char]" `isPrefixOf`) it "doesn't fail on unicode output" $ do diff --git a/test/LangSpec.hs b/test/LangSpec.hs index 9f65b4c..7c624cc 100644 --- a/test/LangSpec.hs +++ b/test/LangSpec.hs @@ -3,10 +3,11 @@ module LangSpec where import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec +import TestUtils spec :: Spec spec = do - describe "listLanguages" $ do - it "lists up language extensions" $ do - exts <- lines <$> listLanguages defaultOptions + describe "languages" $ do + it "contains at lest `OverloadedStrings'" $ do + exts <- runD $ lines <$> languages exts `shouldContain` ["OverloadedStrings"] diff --git a/test/LintSpec.hs b/test/LintSpec.hs index 1812de3..26ca952 100644 --- a/test/LintSpec.hs +++ b/test/LintSpec.hs @@ -2,15 +2,16 @@ module LintSpec where import Language.Haskell.GhcMod import Test.Hspec +import TestUtils spec :: Spec spec = do - describe "lintSyntax" $ do - it "check syntax with HLint" $ do - res <- lintSyntax defaultOptions "test/data/hlint.hs" + 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" - context "without suggestions" $ do - it "doesn't output empty line" $ do - res <- lintSyntax defaultOptions "test/data/ghc-mod-check/Data/Foo.hs" + 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 `shouldBe` "" diff --git a/test/ListSpec.hs b/test/ListSpec.hs index 98ca0ef..1ec4dfd 100644 --- a/test/ListSpec.hs +++ b/test/ListSpec.hs @@ -3,11 +3,11 @@ module ListSpec where import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec +import TestUtils spec :: Spec spec = do - describe "listModules" $ do - it "lists up module names" $ do - cradle <- findCradle - modules <- lines <$> listModules defaultOptions cradle + describe "modules" $ do + it "contains at least `Data.Map'" $ do + modules <- runD $ lines <$> modules modules `shouldContain` ["Data.Map"] diff --git a/test/Main.hs b/test/Main.hs index f7a0820..4e92804 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -7,6 +7,7 @@ import System.Process import Language.Haskell.GhcMod (debugInfo, defaultOptions, findCradle) import Control.Exception as E +import TestUtils main = do let sandboxes = [ "test/data", "test/data/check-packageid" @@ -25,7 +26,7 @@ main = do putStrLn $ "ghc-mod was built with Cabal version " ++ VERSION_Cabal system "ghc --version" - (putStrLn =<< debugInfo defaultOptions =<< findCradle) + (putStrLn =<< runD debugInfo) `E.catch` (\(_ :: E.SomeException) -> return () ) hspec spec From 56ad1a3c8c7eece830c8f9298849c5acc92db5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 04:12:05 +0200 Subject: [PATCH 09/37] Control.Monad.Trans.MonadIO -> CoreMonad.MonadIO they are different before ghc 7.8 --- Language/Haskell/GhcMod/CaseSplit.hs | 2 +- Language/Haskell/GhcMod/FillSig.hs | 2 +- Language/Haskell/GhcMod/Lint.hs | 2 +- Language/Haskell/GhcMod/PkgDoc.hs | 2 +- src/GHCMod.hs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 5067d5e..ce6faee 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -15,7 +15,7 @@ import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils import Language.Haskell.GhcMod.Convert -import MonadUtils (liftIO) +import CoreMonad (liftIO) import Outputable (PprStyle) import qualified Type as Ty import qualified TyCon as Ty diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index 88a83c7..7cb2dc9 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -13,7 +13,7 @@ import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils -import MonadUtils (liftIO) +import CoreMonad (liftIO) import Outputable (PprStyle) import qualified Type as Ty import qualified HsBinds as Ty diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index b88ca5f..ede48c2 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -2,7 +2,7 @@ module Language.Haskell.GhcMod.Lint where import Exception (ghandle) import Control.Exception (SomeException(..)) -import Control.Monad.Trans (liftIO) +import CoreMonad (liftIO) import Language.Haskell.GhcMod.Logger (checkErrorPrefix) import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad diff --git a/Language/Haskell/GhcMod/PkgDoc.hs b/Language/Haskell/GhcMod/PkgDoc.hs index 2f829ab..02f882a 100644 --- a/Language/Haskell/GhcMod/PkgDoc.hs +++ b/Language/Haskell/GhcMod/PkgDoc.hs @@ -1,6 +1,6 @@ module Language.Haskell.GhcMod.PkgDoc (pkgDoc) where -import Control.Monad.Trans (liftIO) +import CoreMonad (liftIO) import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.Monad diff --git a/src/GHCMod.hs b/src/GHCMod.hs index e2e2a4f..2856a87 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -5,7 +5,7 @@ module Main where import Config (cProjectVersion) import Control.Applicative ((<$>)) import Control.Exception (Exception, Handler(..), ErrorCall(..)) -import Control.Monad.Trans (liftIO) +import CoreMonad (liftIO) import qualified Control.Exception as E import Data.Typeable (Typeable) import Data.Version (showVersion) From 320b2243a2b997e6504a376c9f0cf705355404f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 04:51:11 +0200 Subject: [PATCH 10/37] Make GhcMod a special case of GhcModT i.e. turn GhcMod into a monad transformer --- Language/Haskell/GhcMod/Monad.hs | 139 ++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 30 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 5204130..719ca8c 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -1,14 +1,18 @@ {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses, RankNTypes, TypeFamilies #-} +{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-} +{-# LANGUAGE TypeFamilies, UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Haskell.GhcMod.Monad ( GhcMod + , GhcModT , GhcModEnv(..) , GhcModWriter , GhcModState(..) , runGhcMod' , runGhcMod + , runGhcModT' + , runGhcModT , newGhcModEnv , withErrorHandler , toGhcMod @@ -44,10 +48,13 @@ import Control.Monad.Trans.Class (lift) import Data.Monoid (Monoid) #endif -import Control.Monad (liftM) +import Control.Applicative (Alternative) +import Control.Monad (MonadPlus, liftM) import Control.Monad.Base (MonadBase,liftBase) + import Control.Monad.Reader.Class import Control.Monad.State.Class +import Control.Monad.Trans.Class import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..),runRWST) import Control.Monad.Writer.Class @@ -73,16 +80,20 @@ defaultState = GhcModState type GhcModWriter = () ---------------------------------------------------------------- +type GhcMod a = GhcModT IO a -newtype GhcMod a = GhcMod { - unGhcMod :: RWST GhcModEnv GhcModWriter GhcModState IO a +newtype GhcModT m a = GhcModT { + unGhcModT :: RWST GhcModEnv GhcModWriter GhcModState m a } deriving (Functor ,Applicative + ,Alternative ,Monad + ,MonadPlus ,MonadIO ,MonadReader GhcModEnv ,MonadWriter GhcModWriter ,MonadState GhcModState + ,MonadTrans ) #if __GLASGOW_HASKELL__ < 708 @@ -92,13 +103,13 @@ instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where #endif ---------------------------------------------------------------- - -runGhcMod' :: GhcModEnv - -> GhcModState - -> GhcMod a - -> IO (a,(GhcModState, GhcModWriter)) -runGhcMod' r s a = do - (a', s',w) <- runRWST (unGhcMod $ initGhcMonad (Just libdir) >> a) r s +runGhcModT' :: (MonadIO m, MonadBaseControl IO m) + => GhcModEnv + -> GhcModState + -> GhcModT m a + -> m (a,(GhcModState, GhcModWriter)) +runGhcModT' r s a = do + (a',s',w) <- runRWST (unGhcModT $ initGhcMonad (Just libdir) >> a) r s return (a',(s',w)) newGhcModEnv :: Options -> FilePath -> IO GhcModEnv @@ -111,16 +122,24 @@ newGhcModEnv opt dir = do , gmCradle = c } -runGhcMod :: Options -> GhcMod a -> IO a -runGhcMod opt action = do - env <- liftIO $ newGhcModEnv opt =<< getCurrentDirectory - (a,(_,_)) <- runGhcMod' env defaultState $ do +runGhcModT :: (MonadIO m, MonadBaseControl IO m) => Options -> GhcModT m a -> m a +runGhcModT opt action = do + env <- liftBase $ newGhcModEnv opt =<< getCurrentDirectory + (a,(_,_)) <- runGhcModT' env defaultState $ do dflags <- getSessionDynFlags defaultCleanupHandler dflags $ do - toGhcMod $ initializeFlagsWithCradle opt (gmCradle env) + initializeFlagsWithCradle opt (gmCradle env) action return a +runGhcMod' :: GhcModEnv + -> GhcModState + -> GhcModT IO a + -> IO (a,(GhcModState, GhcModWriter)) +runGhcMod' = runGhcModT' + +runGhcMod :: Options -> GhcMod a -> IO a +runGhcMod = runGhcModT ---------------------------------------------------------------- withErrorHandler :: String -> GhcMod a -> GhcMod a @@ -133,7 +152,7 @@ withErrorHandler label = ghandle ignore exitSuccess -- | This is only a transitional mechanism don't use it for new code. -toGhcMod :: Ghc a -> GhcMod a +toGhcMod :: (Functor m, MonadIO m) => Ghc a -> GhcModT m a toGhcMod a = do s <- gmGhcSession <$> ask liftIO $ unGhc a $ Session s @@ -146,30 +165,90 @@ options = gmOptions <$> ask cradle :: GhcMod Cradle cradle = gmCradle <$> ask -instance MonadBase IO GhcMod where - liftBase = GhcMod . liftBase -instance MonadBaseControl IO GhcMod where - newtype StM GhcMod a = StGhcMod { - unStGhcMod :: StM (RWST GhcModEnv () GhcModState IO) a } +instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where + liftBase = GhcModT . liftBase - liftBaseWith f = GhcMod . liftBaseWith $ \runInBase -> - f $ liftM StGhcMod . runInBase . unGhcMod +instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where + newtype StM (GhcModT m) a = StGhcMod { + unStGhcMod :: StM (RWST GhcModEnv () GhcModState m) a } - restoreM = GhcMod . restoreM . unStGhcMod + liftBaseWith f = GhcModT . liftBaseWith $ \runInBase -> + f $ liftM StGhcMod . runInBase . unGhcModT + + restoreM = GhcModT . restoreM . unStGhcMod {-# INLINE liftBaseWith #-} {-# INLINE restoreM #-} -instance GhcMonad GhcMod where - getSession = liftIO . readIORef . gmGhcSession =<< ask - setSession a = liftIO . flip writeIORef a . gmGhcSession =<< ask +-- 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 HasDynFlags GhcMod where +instance (Functor m, MonadIO m, MonadBaseControl IO m) + => HasDynFlags (GhcModT m) where getDynFlags = getSessionDynFlags #endif -instance ExceptionMonad GhcMod where +instance (MonadIO m, MonadBaseControl IO m) + => ExceptionMonad (GhcModT m) where gcatch act handler = control $ \run -> run act `gcatch` (run . handler) From d818a64f6fd25ba41341a1b800fd4099cfe56005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 04:51:27 +0200 Subject: [PATCH 11/37] whitespace --- Language/Haskell/GhcMod/Monad.hs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 719ca8c..9220e22 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -50,13 +50,13 @@ import Data.Monoid (Monoid) import Control.Applicative (Alternative) import Control.Monad (MonadPlus, liftM) -import Control.Monad.Base (MonadBase,liftBase) +import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) -import Control.Monad.Trans.RWS.Lazy (RWST(..),runRWST) +import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Writer.Class import Data.IORef (IORef, readIORef, writeIORef, newIORef) @@ -85,15 +85,15 @@ type GhcMod a = GhcModT IO a newtype GhcModT m a = GhcModT { unGhcModT :: RWST GhcModEnv GhcModWriter GhcModState m a } deriving (Functor - ,Applicative - ,Alternative - ,Monad - ,MonadPlus - ,MonadIO - ,MonadReader GhcModEnv - ,MonadWriter GhcModWriter - ,MonadState GhcModState - ,MonadTrans + , Applicative + , Alternative + , Monad + , MonadPlus + , MonadIO + , MonadReader GhcModEnv + , MonadWriter GhcModWriter + , MonadState GhcModState + , MonadTrans ) #if __GLASGOW_HASKELL__ < 708 From 9f94bc863c167628761bc926e6b9a9c69c5cc09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 05:44:31 +0200 Subject: [PATCH 12/37] Add `Module` type --- Language/Haskell/GhcMod/Browse.hs | 4 ++-- Language/Haskell/GhcMod/Types.hs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Browse.hs b/Language/Haskell/GhcMod/Browse.hs index 49798db..a984235 100644 --- a/Language/Haskell/GhcMod/Browse.hs +++ b/Language/Haskell/GhcMod/Browse.hs @@ -10,7 +10,7 @@ import Data.List (sort) import Data.Maybe (catMaybes) import Exception (ghandle) import FastString (mkFastString) -import GHC (GhcException(CmdLineError), ModuleInfo, Name, TyThing, DynFlags, Type, TyCon, Module) +import GHC (GhcException(CmdLineError), ModuleInfo, Name, TyThing, DynFlags, Type, TyCon) import qualified GHC as G import Language.Haskell.GhcMod.Doc (showPage, showOneLine, styleUnqualified) import Language.Haskell.GhcMod.GHCApi @@ -144,7 +144,7 @@ browseAll dflag = do is <- mapM G.getModuleInfo ms return $ concatMap (toNameModule dflag) (zip ms is) -toNameModule :: DynFlags -> (Module, Maybe ModuleInfo) -> [(String,String)] +toNameModule :: DynFlags -> (G.Module, Maybe ModuleInfo) -> [(String,String)] toNameModule _ (_,Nothing) = [] toNameModule dflag (m,Just inf) = map (\name -> (toStr name, mdl)) names where diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index b8bb908..4dfb161 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -93,6 +93,9 @@ type Expression = String -- | Module name. type ModuleString = String +-- | A Module +type Module = [String] + -- | Option information for GHC data CompilerOptions = CompilerOptions { ghcOptions :: [GHCOption] -- ^ Command line options From 53394d3075d214fa72442f5f8f22ec8525975266 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 11 Jul 2014 14:09:10 +0900 Subject: [PATCH 13/37] ver bumps up. Major version is now 5 because of a lot of API changes. --- ghc-mod.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 1cfb162..5a93d69 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -1,5 +1,5 @@ Name: ghc-mod -Version: 4.1.0 +Version: 5.0.0 Author: Kazu Yamamoto Maintainer: Kazu Yamamoto License: BSD3 From daada0d27edd2f5fb99950593881a36de0f67d2e Mon Sep 17 00:00:00 2001 From: Alejandro Cabrera Date: Fri, 11 Jul 2014 01:12:36 -0400 Subject: [PATCH 14/37] Fix GHC 7.8.3: MatchGroup now has 4 args Rather than use a pattern match, this patch opts to explicitly extract the fields of interest using where syntax. This keeps compatibility across GHC 7.8 releases. Ref: https://github.com/ghc/ghc/commit/eeaea2df3fa585db503034f419c6e4331a4d8a84#diff-259092edcc59456f526cdef255c181d1L909 --- Language/Haskell/GhcMod/Gap.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 062538b..63ef445 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -280,8 +280,10 @@ class HasType a where instance HasType (LHsBind Id) where #if __GLASGOW_HASKELL__ >= 708 - getType _ (L spn FunBind{fun_matches = MG _ in_tys out_typ}) = return $ Just (spn, typ) - where typ = mkFunTys in_tys out_typ + getType _ (L spn FunBind{fun_matches = m}) = return $ Just (spn, typ) + where in_tys = mg_arg_tys m + out_typ = mg_res_ty m + typ = mkFunTys in_tys out_typ #else getType _ (L spn FunBind{fun_matches = MatchGroup _ typ}) = return $ Just (spn, typ) #endif From 73bf4cbc4eccca2583af99a7a5a4326265366c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 10:40:09 +0200 Subject: [PATCH 15/37] Remove `withGhc` and `withGhc'`, they're not used anymore. --- Language/Haskell/GhcMod/GHCApi.hs | 25 ++----------------------- Language/Haskell/GhcMod/Ghc.hs | 5 +---- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/Language/Haskell/GhcMod/GHCApi.hs b/Language/Haskell/GhcMod/GHCApi.hs index 2cd0886..ba98bfe 100644 --- a/Language/Haskell/GhcMod/GHCApi.hs +++ b/Language/Haskell/GhcMod/GHCApi.hs @@ -1,9 +1,7 @@ {-# LANGUAGE ScopedTypeVariables, RecordWildCards #-} module Language.Haskell.GhcMod.GHCApi ( - withGHC - , withGHC' - , initializeFlagsWithCradle + initializeFlagsWithCradle , setTargetFiles , getDynamicFlags , systemLibDir @@ -39,26 +37,6 @@ systemLibDir = libdir ---------------------------------------------------------------- --- | Converting the 'Ghc' monad to the 'IO' monad. -withGHC :: FilePath -- ^ A target file displayed in an error message. - -> Ghc a -- ^ 'Ghc' actions created by the Ghc utilities. - -> IO a -withGHC file body = ghandle ignore $ withGHC' body - where - ignore :: SomeException -> IO a - ignore e = do - hPutStr stderr $ file ++ ":0:0:Error:" - hPrint stderr e - exitSuccess - -withGHC' :: Ghc a -> IO a -withGHC' body = do - G.runGhc (Just systemLibDir) $ do - dflags <- G.getSessionDynFlags - G.defaultCleanupHandler dflags body - ----------------------------------------------------------------- - importDirs :: [IncludeDir] importDirs = [".","..","../..","../../..","../../../..","../../../../.."] @@ -107,6 +85,7 @@ initSession build Options {..} CompilerOptions {..} = do $ setEmptyLogger $ Gap.addPackageFlags depPackages df) + setEmptyLogger :: DynFlags -> DynFlags setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () diff --git a/Language/Haskell/GhcMod/Ghc.hs b/Language/Haskell/GhcMod/Ghc.hs index b2259db..0d2cd20 100644 --- a/Language/Haskell/GhcMod/Ghc.hs +++ b/Language/Haskell/GhcMod/Ghc.hs @@ -1,9 +1,6 @@ module Language.Haskell.GhcMod.Ghc ( - -- * Converting the 'Ghc' monad to the 'IO' monad - withGHC - , withGHC' -- * 'SymMdlDb' - , Symbol + Symbol , SymMdlDb , getSymMdlDb , lookupSym From 81c58585a2a0c048e59f0162379057be80f497ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Jul 2014 10:43:51 +0200 Subject: [PATCH 16/37] Add functions for dealing with packages, modules and bindings to GHCApi --- Language/Haskell/GhcMod/GHCApi.hs | 89 +++++++++++++++++++++++++++++-- Language/Haskell/GhcMod/Types.hs | 6 +++ test/GhcApiSpec.hs | 31 +++++++++++ 3 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 test/GhcApiSpec.hs diff --git a/Language/Haskell/GhcMod/GHCApi.hs b/Language/Haskell/GhcMod/GHCApi.hs index ba98bfe..5fc9f6f 100644 --- a/Language/Haskell/GhcMod/GHCApi.hs +++ b/Language/Haskell/GhcMod/GHCApi.hs @@ -9,6 +9,13 @@ module Language.Haskell.GhcMod.GHCApi ( , withCmdFlags , setNoWaringFlags , setAllWaringFlags + , ghcPkgDb + , package + , modules + , findModule + , moduleInfo + , localModuleInfo + , bindings ) where import Language.Haskell.GhcMod.CabalApi @@ -19,14 +26,17 @@ import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) import Control.Monad (forM, void) +import Distribution.Package (InstalledPackageId(..)) import Data.Maybe (isJust, fromJust) -import Exception (ghandle, SomeException(..)) +import qualified Data.Map.Strict as M import GHC (DynFlags(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) import qualified GHC as G import GhcMonad import GHC.Paths (libdir) -import System.Exit (exitSuccess) -import System.IO (hPutStr, hPrint, stderr) +import qualified Packages as G +import qualified Module as G +import qualified OccName as G + import System.IO.Unsafe (unsafePerformIO) ---------------------------------------------------------------- @@ -178,3 +188,76 @@ allWarningFlags = unsafePerformIO $ do df <- G.getSessionDynFlags df' <- addCmdOpts ["-Wall"] df return $ G.warningFlags df' + +---------------------------------------------------------------- +-- 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 = do + 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 :: GhcMonad m + => Maybe Package + -> ModuleString + -> 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 :: GhcMonad m => ModuleString -> m (Maybe G.ModuleInfo) +localModuleInfo mdl = moduleInfo Nothing mdl + +bindings :: G.ModuleInfo -> [Binding] +bindings minfo = do + map (G.occNameString . G.getOccName) $ G.modInfoExports minfo + + +---------------------------------------------------------------- +-- for PkgDoc + +-- import Distribution.InstalledPackageInfo (showInstalledPackageInfoField) +-- haddockHtml :: GhcMonad m => Package -> m String +-- haddockHtml pkg = do +-- extractField info . fromJust . lookup pkg <$> ghcPkgDb +-- where +-- extractField = fromJust $ showInstalledPackageInfoField "haddock-html" diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 4dfb161..b42b018 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -1,6 +1,9 @@ module Language.Haskell.GhcMod.Types where import Data.List (intercalate) +import qualified Data.Map as M + +import PackageConfig (PackageConfig) -- | Output style. data OutputStyle = LispStyle -- ^ S expression style. @@ -87,6 +90,9 @@ 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) + -- | Haskell expression. type Expression = String diff --git a/test/GhcApiSpec.hs b/test/GhcApiSpec.hs new file mode 100644 index 0000000..71d2c27 --- /dev/null +++ b/test/GhcApiSpec.hs @@ -0,0 +1,31 @@ +module GhcApiSpec where + +import Control.Applicative +import Control.Monad +import Data.List (sort) +import Language.Haskell.GhcMod.GHCApi +import Test.Hspec +import TestUtils +import CoreMonad (liftIO) + +import Dir + +spec :: Spec +spec = do + describe "findModule" $ do + it "finds Data.List in `base' and `haskell2010'" + $ withDirectory_ "test/data" $ runD $ do + pkgs <- findModule "Data.List" <$> ghcPkgDb + let pkgNames = pkgName `map` pkgs + liftIO $ pkgNames `shouldContain` ["base", "haskell2010"] + + describe "moduleInfo" $ do + it "works for modules from global packages (e.g. base:Data.List)" + $ withDirectory_ "test/data" $ runD $ do + Just info <- moduleInfo (Just ("base","","")) "Data.List" + liftIO $ sort (bindings info) `shouldContain` ["++"] + + it "works for local modules" + $ withDirectory_ "test/data" $ runD $ do + Just info <- moduleInfo Nothing "Baz" + liftIO $ bindings info `shouldContain` ["baz"] From 503e8cbe06cd980734a82aeb9f0a70a61071e750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Sat, 12 Jul 2014 02:53:59 +0200 Subject: [PATCH 17/37] Move DynFlag related functions from GHCApi to another module --- Language/Haskell/GhcMod/DynFlags.hs | 108 ++++++++++++++++++++++++++++ Language/Haskell/GhcMod/GHCApi.hs | 105 ++------------------------- ghc-mod.cabal | 1 + 3 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 Language/Haskell/GhcMod/DynFlags.hs diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs new file mode 100644 index 0000000..2589ff2 --- /dev/null +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -0,0 +1,108 @@ +module Language.Haskell.GhcMod.DynFlags where + +import qualified Language.Haskell.GhcMod.Gap as Gap +import Language.Haskell.GhcMod.Types + +import Control.Applicative ((<$>)) +import Control.Monad (forM, void) +import GHC (DynFlags(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) +import qualified GHC as G +import GhcMonad +import GHC.Paths (libdir) + +import System.IO.Unsafe (unsafePerformIO) + +data Build = CabalPkg | SingleFile deriving Eq + +setEmptyLogger :: DynFlags -> DynFlags +setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () + +-- we don't want to generate object code so we compile to bytecode +-- (HscInterpreted) which implies LinkInMemory +-- HscInterpreted +setLinkerOptions :: DynFlags -> DynFlags +setLinkerOptions df = df { + ghcLink = LinkInMemory + , hscTarget = HscInterpreted + } + +setIncludeDirs :: [IncludeDir] -> DynFlags -> DynFlags +setIncludeDirs idirs df = df { importPaths = idirs } + +setBuildEnv :: Build -> DynFlags -> DynFlags +setBuildEnv build = setHideAllPackages build . setCabalPackage build + +-- At the moment with this option set ghc only prints different error messages, +-- suggesting the user to add a hidden package to the build-depends in his cabal +-- file for example +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) + where + tfst (a,_,_) = a + +---------------------------------------------------------------- + +-- | Set the files as targets and load them. +setTargetFiles :: (GhcMonad m) => [FilePath] -> m () +setTargetFiles files = do + targets <- forM files $ \file -> G.guessTarget file Nothing + G.setTargets targets + void $ G.load LoadAllTargets + +---------------------------------------------------------------- + +-- | Return the 'DynFlags' currently in use in the GHC session. +getDynamicFlags :: IO DynFlags +getDynamicFlags = do + G.runGhc (Just libdir) G.getSessionDynFlags + +withDynFlags :: GhcMonad m + => (DynFlags -> DynFlags) + -> m a + -> m a +withDynFlags setFlags body = G.gbracket setup teardown (\_ -> body) + where + setup = do + dflags <- G.getSessionDynFlags + void $ G.setSessionDynFlags (setFlags dflags) + return dflags + teardown = void . G.setSessionDynFlags + +withCmdFlags :: GhcMonad m => [GHCOption] -> m a -> m a +withCmdFlags flags body = G.gbracket setup teardown (\_ -> body) + where + setup = do + dflags <- G.getSessionDynFlags >>= addCmdOpts flags + void $ G.setSessionDynFlags dflags + return dflags + teardown = void . G.setSessionDynFlags + +---------------------------------------------------------------- + +-- | Set 'DynFlags' equivalent to "-w:". +setNoWaringFlags :: DynFlags -> DynFlags +setNoWaringFlags df = df { warningFlags = Gap.emptyWarnFlags} + +-- | Set 'DynFlags' equivalent to "-Wall". +setAllWaringFlags :: DynFlags -> DynFlags +setAllWaringFlags df = df { warningFlags = allWarningFlags } + +allWarningFlags :: Gap.WarnFlags +allWarningFlags = unsafePerformIO $ do + G.runGhc (Just libdir) $ do + df <- G.getSessionDynFlags + df' <- addCmdOpts ["-Wall"] df + return $ G.warningFlags df' + +---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/GHCApi.hs b/Language/Haskell/GhcMod/GHCApi.hs index 5fc9f6f..4a28bfa 100644 --- a/Language/Haskell/GhcMod/GHCApi.hs +++ b/Language/Haskell/GhcMod/GHCApi.hs @@ -21,15 +21,16 @@ module Language.Haskell.GhcMod.GHCApi ( import Language.Haskell.GhcMod.CabalApi import Language.Haskell.GhcMod.GHCChoice import Language.Haskell.GhcMod.GhcPkg +import Language.Haskell.GhcMod.DynFlags import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) -import Control.Monad (forM, void) +import Control.Monad (void) import Distribution.Package (InstalledPackageId(..)) import Data.Maybe (isJust, fromJust) -import qualified Data.Map.Strict as M -import GHC (DynFlags(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) +import qualified Data.Map as M +import GHC (DynFlags(..)) import qualified GHC as G import GhcMonad import GHC.Paths (libdir) @@ -37,8 +38,6 @@ import qualified Packages as G import qualified Module as G import qualified OccName as G -import System.IO.Unsafe (unsafePerformIO) - ---------------------------------------------------------------- -- | Obtaining the directory for system libraries. @@ -50,8 +49,6 @@ systemLibDir = libdir importDirs :: [IncludeDir] importDirs = [".","..","../..","../../..","../../../..","../../../../.."] -data Build = CabalPkg | SingleFile deriving Eq - -- | Initialize the 'DynFlags' relating to the compilation of a single -- file or GHC session according to the 'Cradle' and 'Options' -- provided. @@ -95,100 +92,6 @@ initSession build Options {..} CompilerOptions {..} = do $ setEmptyLogger $ Gap.addPackageFlags depPackages df) - -setEmptyLogger :: DynFlags -> DynFlags -setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () - ----------------------------------------------------------------- - --- we don't want to generate object code so we compile to bytecode --- (HscInterpreted) which implies LinkInMemory --- HscInterpreted -setLinkerOptions :: DynFlags -> DynFlags -setLinkerOptions df = df { - ghcLink = LinkInMemory - , hscTarget = HscInterpreted - } - -setIncludeDirs :: [IncludeDir] -> DynFlags -> DynFlags -setIncludeDirs idirs df = df { importPaths = idirs } - -setBuildEnv :: Build -> DynFlags -> DynFlags -setBuildEnv build = setHideAllPackages build . setCabalPackage build - --- At the moment with this option set ghc only prints different error messages, --- suggesting the user to add a hidden package to the build-depends in his cabal --- file for example -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) - where - tfst (a,_,_) = a - ----------------------------------------------------------------- - --- | Set the files as targets and load them. -setTargetFiles :: (GhcMonad m) => [FilePath] -> m () -setTargetFiles files = do - targets <- forM files $ \file -> G.guessTarget file Nothing - G.setTargets targets - void $ G.load LoadAllTargets - ----------------------------------------------------------------- - --- | Return the 'DynFlags' currently in use in the GHC session. -getDynamicFlags :: IO DynFlags -getDynamicFlags = do - G.runGhc (Just systemLibDir) G.getSessionDynFlags - -withDynFlags :: GhcMonad m - => (DynFlags -> DynFlags) - -> m a - -> m a -withDynFlags setFlags body = G.gbracket setup teardown (\_ -> body) - where - setup = do - dflags <- G.getSessionDynFlags - void $ G.setSessionDynFlags (setFlags dflags) - return dflags - teardown = void . G.setSessionDynFlags - -withCmdFlags :: GhcMonad m => [GHCOption] -> m a -> m a -withCmdFlags flags body = G.gbracket setup teardown (\_ -> body) - where - setup = do - dflags <- G.getSessionDynFlags >>= addCmdOpts flags - void $ G.setSessionDynFlags dflags - return dflags - teardown = void . G.setSessionDynFlags - ----------------------------------------------------------------- - --- | Set 'DynFlags' equivalent to "-w:". -setNoWaringFlags :: DynFlags -> DynFlags -setNoWaringFlags df = df { warningFlags = Gap.emptyWarnFlags} - --- | Set 'DynFlags' equivalent to "-Wall". -setAllWaringFlags :: DynFlags -> DynFlags -setAllWaringFlags df = df { warningFlags = allWarningFlags } - -allWarningFlags :: Gap.WarnFlags -allWarningFlags = unsafePerformIO $ do - G.runGhc (Just systemLibDir) $ do - df <- G.getSessionDynFlags - df' <- addCmdOpts ["-Wall"] df - return $ G.warningFlags df' - ---------------------------------------------------------------- -- get Packages,Modules,Bindings diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 1cfb162..8273417 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -67,6 +67,7 @@ Library Language.Haskell.GhcMod.Convert Language.Haskell.GhcMod.Debug Language.Haskell.GhcMod.Doc + Language.Haskell.GhcMod.DynFlags Language.Haskell.GhcMod.FillSig Language.Haskell.GhcMod.Find Language.Haskell.GhcMod.Flag From b6896a481a7fe14ab4db420b7304ac3f7abdd7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Sat, 12 Jul 2014 03:30:06 +0200 Subject: [PATCH 18/37] Move `initializeFlagsWithCradle` to Monad.hs --- Language/Haskell/GhcMod/Browse.hs | 2 +- Language/Haskell/GhcMod/Check.hs | 2 +- Language/Haskell/GhcMod/Debug.hs | 6 +-- Language/Haskell/GhcMod/GHCApi.hs | 81 +---------------------------- Language/Haskell/GhcMod/Ghc.hs | 1 - Language/Haskell/GhcMod/Internal.hs | 12 +++-- Language/Haskell/GhcMod/Logger.hs | 2 +- Language/Haskell/GhcMod/Monad.hs | 60 +++++++++++++++++++-- Language/Haskell/GhcMod/SrcUtils.hs | 2 +- 9 files changed, 71 insertions(+), 97 deletions(-) diff --git a/Language/Haskell/GhcMod/Browse.hs b/Language/Haskell/GhcMod/Browse.hs index a984235..72419b8 100644 --- a/Language/Haskell/GhcMod/Browse.hs +++ b/Language/Haskell/GhcMod/Browse.hs @@ -13,7 +13,7 @@ import FastString (mkFastString) import GHC (GhcException(CmdLineError), ModuleInfo, Name, TyThing, DynFlags, Type, TyCon) import qualified GHC as G import Language.Haskell.GhcMod.Doc (showPage, showOneLine, styleUnqualified) -import Language.Haskell.GhcMod.GHCApi +import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Convert diff --git a/Language/Haskell/GhcMod/Check.hs b/Language/Haskell/GhcMod/Check.hs index 48dacc3..736c42f 100644 --- a/Language/Haskell/GhcMod/Check.hs +++ b/Language/Haskell/GhcMod/Check.hs @@ -6,7 +6,7 @@ module Language.Haskell.GhcMod.Check ( ) where import Control.Applicative ((<$>)) -import Language.Haskell.GhcMod.GHCApi +import Language.Haskell.GhcMod.DynFlags import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index b3ce715..2278ad2 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -4,12 +4,10 @@ import Control.Applicative ((<$>)) import CoreMonad (liftIO) import Data.List (intercalate) import Data.Maybe (isJust, fromJust) -import Language.Haskell.GhcMod.CabalApi -import Language.Haskell.GhcMod.GHCApi -import Language.Haskell.GhcMod.GHCChoice ((||>)) import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Internal ---------------------------------------------------------------- @@ -28,7 +26,7 @@ debugInfo = cradle >>= \c -> convert' =<< do , "GHC options: " ++ unwords gopts , "Include directories: " ++ unwords incDir , "Dependent packages: " ++ intercalate ", " (map showPkg pkgs) - , "System libraries: " ++ systemLibDir + , "System libraries: " ++ ghcLibDir ] where simpleCompilerOption = options >>= \op -> diff --git a/Language/Haskell/GhcMod/GHCApi.hs b/Language/Haskell/GhcMod/GHCApi.hs index 4a28bfa..b765698 100644 --- a/Language/Haskell/GhcMod/GHCApi.hs +++ b/Language/Haskell/GhcMod/GHCApi.hs @@ -1,15 +1,7 @@ {-# LANGUAGE ScopedTypeVariables, RecordWildCards #-} module Language.Haskell.GhcMod.GHCApi ( - initializeFlagsWithCradle - , setTargetFiles - , getDynamicFlags - , systemLibDir - , withDynFlags - , withCmdFlags - , setNoWaringFlags - , setAllWaringFlags - , ghcPkgDb + ghcPkgDb , package , modules , findModule @@ -18,80 +10,20 @@ module Language.Haskell.GhcMod.GHCApi ( , bindings ) where -import Language.Haskell.GhcMod.CabalApi -import Language.Haskell.GhcMod.GHCChoice import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.DynFlags -import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) -import Control.Monad (void) import Distribution.Package (InstalledPackageId(..)) -import Data.Maybe (isJust, fromJust) import qualified Data.Map as M import GHC (DynFlags(..)) import qualified GHC as G import GhcMonad -import GHC.Paths (libdir) import qualified Packages as G import qualified Module as G import qualified OccName as G ----------------------------------------------------------------- - --- | Obtaining the directory for system libraries. -systemLibDir :: FilePath -systemLibDir = libdir - ----------------------------------------------------------------- - -importDirs :: [IncludeDir] -importDirs = [".","..","../..","../../..","../../../..","../../../../.."] - --- | Initialize the 'DynFlags' relating to the compilation of a single --- file or GHC session according to the 'Cradle' and 'Options' --- provided. -initializeFlagsWithCradle :: GhcMonad m - => Options - -> Cradle - -> m () -initializeFlagsWithCradle opt cradle - | cabal = withCabal |||> withSandbox - | otherwise = withSandbox - where - mCradleFile = cradleCabalFile cradle - cabal = isJust mCradleFile - ghcopts = ghcOpts opt - withCabal = do - pkgDesc <- liftIO $ parseCabalFile $ fromJust mCradleFile - compOpts <- liftIO $ getCompilerOptions ghcopts cradle pkgDesc - initSession CabalPkg opt compOpts - withSandbox = initSession SingleFile opt compOpts - where - pkgOpts = ghcDbStackOpts $ cradlePkgDbStack cradle - compOpts - | null pkgOpts = CompilerOptions ghcopts importDirs [] - | otherwise = CompilerOptions (ghcopts ++ pkgOpts) [wdir,rdir] [] - wdir = cradleCurrentDir cradle - rdir = cradleRootDir cradle - ----------------------------------------------------------------- - -initSession :: GhcMonad m - => Build - -> Options - -> CompilerOptions - -> m () -initSession build Options {..} CompilerOptions {..} = do - df <- G.getSessionDynFlags - void $ G.setSessionDynFlags =<< (addCmdOpts ghcOptions - $ setLinkerOptions - $ setIncludeDirs includeDirs - $ setBuildEnv build - $ setEmptyLogger - $ Gap.addPackageFlags depPackages df) - ---------------------------------------------------------------- -- get Packages,Modules,Bindings @@ -153,14 +85,3 @@ localModuleInfo mdl = moduleInfo Nothing mdl bindings :: G.ModuleInfo -> [Binding] bindings minfo = do map (G.occNameString . G.getOccName) $ G.modInfoExports minfo - - ----------------------------------------------------------------- --- for PkgDoc - --- import Distribution.InstalledPackageInfo (showInstalledPackageInfoField) --- haddockHtml :: GhcMonad m => Package -> m String --- haddockHtml pkg = do --- extractField info . fromJust . lookup pkg <$> ghcPkgDb --- where --- extractField = fromJust $ showInstalledPackageInfoField "haddock-html" diff --git a/Language/Haskell/GhcMod/Ghc.hs b/Language/Haskell/GhcMod/Ghc.hs index 0d2cd20..074a218 100644 --- a/Language/Haskell/GhcMod/Ghc.hs +++ b/Language/Haskell/GhcMod/Ghc.hs @@ -8,4 +8,3 @@ module Language.Haskell.GhcMod.Ghc ( ) where import Language.Haskell.GhcMod.Find -import Language.Haskell.GhcMod.GHCApi diff --git a/Language/Haskell/GhcMod/Internal.hs b/Language/Haskell/GhcMod/Internal.hs index 99566e6..a405ff5 100644 --- a/Language/Haskell/GhcMod/Internal.hs +++ b/Language/Haskell/GhcMod/Internal.hs @@ -17,11 +17,9 @@ module Language.Haskell.GhcMod.Internal ( , cabalSourceDirs , cabalAllTargets -- * GHC.Paths - , systemLibDir + , ghcLibDir -- * IO , getDynamicFlags - -- * Initializing 'DynFlags' - , initializeFlagsWithCradle -- * Targets , setTargetFiles -- * Logging @@ -36,8 +34,14 @@ module Language.Haskell.GhcMod.Internal ( , (|||>) ) where +import GHC.Paths (libdir) + import Language.Haskell.GhcMod.CabalApi -import Language.Haskell.GhcMod.GHCApi +import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.GHCChoice import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Types + +-- | Obtaining the directory for ghc system libraries. +ghcLibDir :: FilePath +ghcLibDir = libdir diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index dfe0363..5d16788 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -17,7 +17,7 @@ import GHC (DynFlags, SrcSpan, Severity(SevError)) import qualified GHC as G import HscTypes (SourceError, srcErrorMessages) import Language.Haskell.GhcMod.Doc (showPage, getStyle) -import Language.Haskell.GhcMod.GHCApi (withDynFlags, withCmdFlags) +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 diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 9220e22..bf92caa 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -1,6 +1,6 @@ {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-} {-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-} -{-# LANGUAGE TypeFamilies, UndecidableInstances #-} +{-# LANGUAGE TypeFamilies, UndecidableInstances, RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Haskell.GhcMod.Monad ( @@ -23,13 +23,18 @@ module Language.Haskell.GhcMod.Monad ( , module Control.Monad.State.Class ) where -import Language.Haskell.GhcMod.Cradle -import Language.Haskell.GhcMod.GHCApi import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Cradle +import Language.Haskell.GhcMod.DynFlags +import Language.Haskell.GhcMod.GhcPkg +import Language.Haskell.GhcMod.GHCChoice +import Language.Haskell.GhcMod.CabalApi +import qualified Language.Haskell.GhcMod.Gap as Gap import DynFlags import Exception import GHC +import qualified GHC as G import GHC.Paths (libdir) import GhcMonad #if __GLASGOW_HASKELL__ <= 702 @@ -49,7 +54,7 @@ import Data.Monoid (Monoid) #endif import Control.Applicative (Alternative) -import Control.Monad (MonadPlus, liftM) +import Control.Monad (MonadPlus, liftM, void) import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Reader.Class @@ -59,6 +64,7 @@ import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, con import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Writer.Class +import Data.Maybe (fromJust, isJust) import Data.IORef (IORef, readIORef, writeIORef, newIORef) import System.Exit (exitSuccess) import System.IO (hPutStr, hPrint, stderr) @@ -80,6 +86,7 @@ defaultState = GhcModState type GhcModWriter = () ---------------------------------------------------------------- + type GhcMod a = GhcModT IO a newtype GhcModT m a = GhcModT { @@ -103,6 +110,51 @@ instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where #endif ---------------------------------------------------------------- + +-- | Initialize the 'DynFlags' relating to the compilation of a single +-- file or GHC session according to the 'Cradle' and 'Options' +-- provided. +initializeFlagsWithCradle :: GhcMonad m + => Options + -> Cradle + -> m () +initializeFlagsWithCradle opt c + | cabal = withCabal |||> withSandbox + | otherwise = withSandbox + where + mCradleFile = cradleCabalFile c + cabal = isJust mCradleFile + ghcopts = ghcOpts opt + withCabal = do + pkgDesc <- liftIO $ parseCabalFile $ fromJust mCradleFile + compOpts <- liftIO $ 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 + +initSession :: GhcMonad m + => Build + -> Options + -> CompilerOptions + -> m () +initSession build Options {..} CompilerOptions {..} = do + df <- G.getSessionDynFlags + void $ G.setSessionDynFlags =<< (addCmdOpts ghcOptions + $ setLinkerOptions + $ setIncludeDirs includeDirs + $ setBuildEnv build + $ setEmptyLogger + $ Gap.addPackageFlags depPackages df) + +---------------------------------------------------------------- + runGhcModT' :: (MonadIO m, MonadBaseControl IO m) => GhcModEnv -> GhcModState diff --git a/Language/Haskell/GhcMod/SrcUtils.hs b/Language/Haskell/GhcMod/SrcUtils.hs index c5438a2..059bcad 100644 --- a/Language/Haskell/GhcMod/SrcUtils.hs +++ b/Language/Haskell/GhcMod/SrcUtils.hs @@ -13,7 +13,7 @@ import GhcMonad import qualified GHC as G import GHC.SYB.Utils (Stage(..), everythingStaged) import Language.Haskell.GhcMod.Doc (showOneLine, getStyle) -import Language.Haskell.GhcMod.GHCApi +import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.Gap (HasType(..), setWarnTypedHoles, setDeferTypeErrors) import qualified Language.Haskell.GhcMod.Gap as Gap import Outputable (PprStyle) From f0bfcb88115bc1f78553aa4fbbcd25f3aadf7e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Sat, 12 Jul 2014 11:16:16 +0200 Subject: [PATCH 19/37] Use GhcModT everywhere and remove the GhcMod alias Not doing this makes having GhcModT pretty pointless as users of the library wouldn't be able to use custom inner monads as evey function for dealing with GhcModT's would be constraint to (GhcModT IO) thus only allowing IO as the inner monad. --- Language/Haskell/GhcMod/Boot.hs | 2 +- Language/Haskell/GhcMod/Browse.hs | 15 ++++---- Language/Haskell/GhcMod/CaseSplit.hs | 5 +-- Language/Haskell/GhcMod/Check.hs | 20 ++++++----- Language/Haskell/GhcMod/Convert.hs | 2 +- Language/Haskell/GhcMod/Debug.hs | 4 +-- Language/Haskell/GhcMod/FillSig.hs | 5 +-- Language/Haskell/GhcMod/Find.hs | 4 +-- Language/Haskell/GhcMod/Flag.hs | 2 +- Language/Haskell/GhcMod/Info.hs | 10 +++--- Language/Haskell/GhcMod/Lang.hs | 2 +- Language/Haskell/GhcMod/Lint.hs | 5 +-- Language/Haskell/GhcMod/List.hs | 2 +- Language/Haskell/GhcMod/Logger.hs | 11 +++--- Language/Haskell/GhcMod/Monad.hs | 35 +++++++------------ Language/Haskell/GhcMod/PkgDoc.hs | 2 +- ghc-mod.cabal | 5 +++ src/GHCMod.hs | 4 +-- src/GHCModi.hs | 51 ++++++++++++++++------------ test/TestUtils.hs | 12 ++++--- 20 files changed, 106 insertions(+), 92 deletions(-) diff --git a/Language/Haskell/GhcMod/Boot.hs b/Language/Haskell/GhcMod/Boot.hs index fd9345c..a95429c 100644 --- a/Language/Haskell/GhcMod/Boot.hs +++ b/Language/Haskell/GhcMod/Boot.hs @@ -8,7 +8,7 @@ import Language.Haskell.GhcMod.List import Language.Haskell.GhcMod.Monad -- | Printing necessary information for front-end booting. -boot :: GhcMod String +boot :: IOish m => GhcModT m String boot = concat <$> sequence [modules, languages, flags, concat <$> mapM browse preBrowsedModules] diff --git a/Language/Haskell/GhcMod/Browse.hs b/Language/Haskell/GhcMod/Browse.hs index 72419b8..aa0d1df 100644 --- a/Language/Haskell/GhcMod/Browse.hs +++ b/Language/Haskell/GhcMod/Browse.hs @@ -28,8 +28,9 @@ import Type (dropForAlls, splitFunTy_maybe, mkFunTy, isPredTy) -- | 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 :: ModuleString -- ^ A module name. (e.g. \"Data.List\") - -> GhcMod String +browse :: IOish m + => ModuleString -- ^ A module name. (e.g. \"Data.List\") + -> GhcModT m String browse pkgmdl = convert' . sort =<< (listExports =<< getModule) where (mpkg,mdl) = splitPkgMdl pkgmdl @@ -61,7 +62,7 @@ splitPkgMdl pkgmdl = case break (==':') pkgmdl of (mdl,"") -> (Nothing,mdl) (pkg,_:mdl) -> (Just pkg,mdl) -processExports :: ModuleInfo -> GhcMod [String] +processExports :: IOish m => ModuleInfo -> GhcModT m [String] processExports minfo = do opt <- options let @@ -70,13 +71,13 @@ processExports minfo = do | otherwise = filter (isAlpha . head . getOccString) mapM (showExport opt minfo) $ removeOps $ G.modInfoExports minfo -showExport :: Options -> ModuleInfo -> Name -> GhcMod String +showExport :: IOish m => Options -> ModuleInfo -> Name -> GhcModT 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 :: GhcMod (Maybe String) + mtype :: IOish m => GhcModT m (Maybe String) mtype | detailed opt = do tyInfo <- G.modInfoLookupName minfo e @@ -91,7 +92,7 @@ showExport opt minfo e = do | isAlpha n = nm | otherwise = "(" ++ nm ++ ")" formatOp "" = error "formatOp" - inOtherModule :: Name -> GhcMod (Maybe TyThing) + inOtherModule :: IOish m => Name -> GhcModT m (Maybe TyThing) inOtherModule nm = G.getModuleInfo (G.nameModule nm) >> G.lookupGlobalName nm justIf :: a -> Bool -> Maybe a justIf x True = Just x @@ -138,7 +139,7 @@ showOutputable dflag = unwords . lines . showPage dflag styleUnqualified . ppr ---------------------------------------------------------------- -- | Browsing all functions in all system/user modules. -browseAll :: DynFlags -> GhcMod [(String,String)] +browseAll :: IOish m => DynFlags -> GhcModT m [(String,String)] browseAll dflag = do ms <- G.packageDbModules True is <- mapM G.getModuleInfo ms diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index ce6faee..706d18d 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -33,10 +33,11 @@ data SplitToTextInfo = SplitToTextInfo { sVarName :: String } -- | Splitting a variable in a equation. -splits :: FilePath -- ^ A target file. +splits :: IOish m + => FilePath -- ^ A target file. -> Int -- ^ Line number. -> Int -- ^ Column number. - -> GhcMod String + -> GhcModT m String splits file lineNo colNo = ghandle handler body where body = inModuleContext file $ \dflag style -> do diff --git a/Language/Haskell/GhcMod/Check.hs b/Language/Haskell/GhcMod/Check.hs index 736c42f..66c7b71 100644 --- a/Language/Haskell/GhcMod/Check.hs +++ b/Language/Haskell/GhcMod/Check.hs @@ -15,8 +15,9 @@ import Language.Haskell.GhcMod.Monad -- | Checking syntax of a target file using GHC. -- Warnings and errors are returned. -checkSyntax :: [FilePath] -- ^ The target files. - -> GhcMod String +checkSyntax :: IOish m + => [FilePath] -- ^ The target files. + -> GhcModT m String checkSyntax [] = return "" checkSyntax files = withErrorHandler sessionName $ do either id id <$> check files @@ -29,8 +30,9 @@ checkSyntax files = withErrorHandler sessionName $ do -- | Checking syntax of a target file using GHC. -- Warnings and errors are returned. -check :: [FilePath] -- ^ The target files. - -> GhcMod (Either String String) +check :: IOish m + => [FilePath] -- ^ The target files. + -> GhcModT m (Either String String) check fileNames = do withLogger setAllWaringFlags $ do setTargetFiles fileNames @@ -38,8 +40,9 @@ check fileNames = do ---------------------------------------------------------------- -- | Expanding Haskell Template. -expandTemplate :: [FilePath] -- ^ The target files. - -> GhcMod String +expandTemplate :: IOish m + => [FilePath] -- ^ The target files. + -> GhcModT m String expandTemplate [] = return "" expandTemplate files = withErrorHandler sessionName $ do either id id <$> expand files @@ -51,7 +54,8 @@ expandTemplate files = withErrorHandler sessionName $ do ---------------------------------------------------------------- -- | Expanding Haskell Template. -expand :: [FilePath] -- ^ The target files. - -> GhcMod (Either String String) +expand :: IOish m + => [FilePath] -- ^ The target files. + -> GhcModT m (Either String String) expand fileNames = withLogger (Gap.setDumpSplices . setNoWaringFlags) $ setTargetFiles fileNames diff --git a/Language/Haskell/GhcMod/Convert.hs b/Language/Haskell/GhcMod/Convert.hs index e348eca..ee1398b 100644 --- a/Language/Haskell/GhcMod/Convert.hs +++ b/Language/Haskell/GhcMod/Convert.hs @@ -23,7 +23,7 @@ inter :: Char -> [Builder] -> Builder inter _ [] = id inter c bs = foldr1 (\x y -> x . (c:) . y) bs -convert' :: ToString a => a -> GhcMod String +convert' :: (ToString a, IOish m) => a -> GhcModT m String convert' x = flip convert x <$> options convert :: ToString a => Options -> a -> String diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 2278ad2..994411d 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -12,7 +12,7 @@ import Language.Haskell.GhcMod.Internal ---------------------------------------------------------------- -- | Obtaining debug information. -debugInfo :: GhcMod String +debugInfo :: IOish m => GhcModT m String debugInfo = cradle >>= \c -> convert' =<< do CompilerOptions gopts incDir pkgs <- if isJust $ cradleCabalFile c then @@ -38,5 +38,5 @@ debugInfo = cradle >>= \c -> convert' =<< do ---------------------------------------------------------------- -- | Obtaining root information. -rootInfo :: GhcMod String +rootInfo :: IOish m => GhcModT m String rootInfo = convert' =<< cradleRootDir <$> cradle diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index 7cb2dc9..fc12384 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -38,10 +38,11 @@ data SigInfo = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName) data HESigInfo = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo) -- | Create a initial body from a signature. -sig :: FilePath -- ^ A target file. +sig :: IOish m + => FilePath -- ^ A target file. -> Int -- ^ Line number. -> Int -- ^ Column number. - -> GhcMod String + -> GhcModT m String sig file lineNo colNo = ghandle handler body where body = inModuleContext file $ \dflag style -> do diff --git a/Language/Haskell/GhcMod/Find.hs b/Language/Haskell/GhcMod/Find.hs index 850d5df..d414e40 100644 --- a/Language/Haskell/GhcMod/Find.hs +++ b/Language/Haskell/GhcMod/Find.hs @@ -31,11 +31,11 @@ type Symbol = String newtype SymMdlDb = SymMdlDb (Map Symbol [ModuleString]) -- | Finding modules to which the symbol belong. -findSymbol :: Symbol -> GhcMod String +findSymbol :: IOish m => Symbol -> GhcModT m String findSymbol sym = convert' =<< lookupSym sym <$> getSymMdlDb -- | Creating 'SymMdlDb'. -getSymMdlDb :: GhcMod SymMdlDb +getSymMdlDb :: IOish m => GhcModT m SymMdlDb getSymMdlDb = do sm <- G.getSessionDynFlags >>= browseAll #if MIN_VERSION_containers(0,5,0) diff --git a/Language/Haskell/GhcMod/Flag.hs b/Language/Haskell/GhcMod/Flag.hs index 74319e8..5fc3e2b 100644 --- a/Language/Haskell/GhcMod/Flag.hs +++ b/Language/Haskell/GhcMod/Flag.hs @@ -6,7 +6,7 @@ import Language.Haskell.GhcMod.Monad -- | Listing GHC flags. (e.g -fno-warn-orphans) -flags :: GhcMod String +flags :: IOish m => GhcModT m String flags = convert' [ "-f" ++ prefix ++ option | option <- Gap.fOptions , prefix <- ["","no-"] diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index 8b4afec..b58b53f 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -21,9 +21,10 @@ import Language.Haskell.GhcMod.Convert ---------------------------------------------------------------- -- | Obtaining information of a target expression. (GHCi's info:) -info :: FilePath -- ^ A target file. +info :: IOish m + => FilePath -- ^ A target file. -> Expression -- ^ A Haskell expression. - -> GhcMod String + -> GhcModT m String info file expr = do opt <- options convert opt <$> ghandle handler body @@ -36,10 +37,11 @@ info file expr = do ---------------------------------------------------------------- -- | Obtaining type of a target expression. (GHCi's type:) -types :: FilePath -- ^ A target file. +types :: IOish m + => FilePath -- ^ A target file. -> Int -- ^ Line number. -> Int -- ^ Column number. - -> GhcMod String + -> GhcModT m String types file lineNo colNo = do opt <- options convert opt <$> ghandle handler body diff --git a/Language/Haskell/GhcMod/Lang.hs b/Language/Haskell/GhcMod/Lang.hs index 071e178..badecbd 100644 --- a/Language/Haskell/GhcMod/Lang.hs +++ b/Language/Haskell/GhcMod/Lang.hs @@ -6,5 +6,5 @@ import Language.Haskell.GhcMod.Monad -- | Listing language extensions. -languages :: GhcMod String +languages :: IOish m => GhcModT m String languages = convert' supportedLanguagesAndExtensions diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index ede48c2..cfa915f 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -11,8 +11,9 @@ import Language.Haskell.HLint (hlint) -- | Checking syntax of a target file using hlint. -- Warnings and errors are returned. -lint :: FilePath -- ^ A target file. - -> GhcMod String +lint :: IOish m + => FilePath -- ^ A target file. + -> GhcModT m String lint file = do opt <- options ghandle handler . pack =<< (liftIO $ hlint $ file : "--quiet" : hlintOpts opt) diff --git a/Language/Haskell/GhcMod/List.hs b/Language/Haskell/GhcMod/List.hs index be0b4c7..ec93ae3 100644 --- a/Language/Haskell/GhcMod/List.hs +++ b/Language/Haskell/GhcMod/List.hs @@ -13,7 +13,7 @@ import UniqFM (eltsUFM) ---------------------------------------------------------------- -- | Listing installed modules. -modules :: GhcMod String +modules :: IOish m => GhcModT m String modules = do opt <- options convert opt . (arrange opt) <$> (getModules `G.gcatch` handler) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 5d16788..2d7121e 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -34,7 +34,7 @@ newtype LogRef = LogRef (IORef Builder) newLogRef :: IO LogRef newLogRef = LogRef <$> newIORef id -readAndClearLogRef :: LogRef -> GhcMod String +readAndClearLogRef :: IOish m => LogRef -> GhcModT m String readAndClearLogRef (LogRef ref) = do b <- liftIO $ readIORef ref liftIO $ writeIORef ref id @@ -50,9 +50,10 @@ appendLogRef df (LogRef ref) _ sev src style msg = do -- | 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 :: (DynFlags -> DynFlags) - -> GhcMod () - -> GhcMod (Either String String) +withLogger :: IOish m + => (DynFlags -> DynFlags) + -> GhcModT m () + -> GhcModT m (Either String String) withLogger setDF body = ghandle sourceError $ do logref <- liftIO $ newLogRef wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcOpts <$> options @@ -65,7 +66,7 @@ withLogger setDF body = ghandle sourceError $ do ---------------------------------------------------------------- -- | Converting 'SourceError' to 'String'. -sourceError :: SourceError -> GhcMod (Either String String) +sourceError :: IOish m => SourceError -> GhcModT m (Either String String) sourceError err = do dflags <- G.getSessionDynFlags style <- toGhcMod getStyle diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index bf92caa..8b69921 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -4,13 +4,11 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Haskell.GhcMod.Monad ( - GhcMod - , GhcModT + GhcModT + , IOish , GhcModEnv(..) , GhcModWriter , GhcModState(..) - , runGhcMod' - , runGhcMod , runGhcModT' , runGhcModT , newGhcModEnv @@ -60,7 +58,8 @@ import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class -import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) +import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, + control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Writer.Class @@ -87,7 +86,7 @@ type GhcModWriter = () ---------------------------------------------------------------- -type GhcMod a = GhcModT IO a +type IOish m = (Functor m, MonadIO m, MonadBaseControl IO m) newtype GhcModT m a = GhcModT { unGhcModT :: RWST GhcModEnv GhcModWriter GhcModState m a @@ -155,7 +154,7 @@ initSession build Options {..} CompilerOptions {..} = do ---------------------------------------------------------------- -runGhcModT' :: (MonadIO m, MonadBaseControl IO m) +runGhcModT' :: IOish m => GhcModEnv -> GhcModState -> GhcModT m a @@ -174,7 +173,7 @@ newGhcModEnv opt dir = do , gmCradle = c } -runGhcModT :: (MonadIO m, MonadBaseControl IO m) => Options -> GhcModT m a -> m a +runGhcModT :: IOish m => Options -> GhcModT m a -> m a runGhcModT opt action = do env <- liftBase $ newGhcModEnv opt =<< getCurrentDirectory (a,(_,_)) <- runGhcModT' env defaultState $ do @@ -183,41 +182,31 @@ runGhcModT opt action = do initializeFlagsWithCradle opt (gmCradle env) action return a - -runGhcMod' :: GhcModEnv - -> GhcModState - -> GhcModT IO a - -> IO (a,(GhcModState, GhcModWriter)) -runGhcMod' = runGhcModT' - -runGhcMod :: Options -> GhcMod a -> IO a -runGhcMod = runGhcModT ---------------------------------------------------------------- -withErrorHandler :: String -> GhcMod a -> GhcMod a +withErrorHandler :: IOish m => String -> GhcModT m a -> GhcModT m a withErrorHandler label = ghandle ignore where - ignore :: SomeException -> GhcMod a + ignore :: IOish m => SomeException -> GhcModT m a ignore e = liftIO $ do hPutStr stderr $ label ++ ":0:0:Error:" hPrint stderr e exitSuccess -- | This is only a transitional mechanism don't use it for new code. -toGhcMod :: (Functor m, MonadIO m) => Ghc a -> GhcModT m a +toGhcMod :: IOish m => Ghc a -> GhcModT m a toGhcMod a = do s <- gmGhcSession <$> ask liftIO $ unGhc a $ Session s ---------------------------------------------------------------- -options :: GhcMod Options +options :: IOish m => GhcModT m Options options = gmOptions <$> ask -cradle :: GhcMod Cradle +cradle :: IOish m => GhcModT m Cradle cradle = gmCradle <$> ask - instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where liftBase = GhcModT . liftBase diff --git a/Language/Haskell/GhcMod/PkgDoc.hs b/Language/Haskell/GhcMod/PkgDoc.hs index 02f882a..b29e172 100644 --- a/Language/Haskell/GhcMod/PkgDoc.hs +++ b/Language/Haskell/GhcMod/PkgDoc.hs @@ -9,7 +9,7 @@ import Control.Applicative ((<$>)) import System.Process (readProcess) -- | Obtaining the package name and the doc path of a module. -pkgDoc :: String -> GhcMod String +pkgDoc :: IOish m => String -> GhcModT m String pkgDoc mdl = cradle >>= \c -> liftIO $ do pkg <- trim <$> readProcess "ghc-pkg" (toModuleOpts c) [] if pkg == "" then diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 8273417..f154bcb 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -51,6 +51,7 @@ Extra-Source-Files: ChangeLog Library Default-Language: Haskell2010 GHC-Options: -Wall + Extensions: ConstraintKinds, FlexibleContexts Exposed-Modules: Language.Haskell.GhcMod Language.Haskell.GhcMod.Ghc Language.Haskell.GhcMod.Monad @@ -117,6 +118,7 @@ Executable ghc-mod Main-Is: GHCMod.hs Other-Modules: Paths_ghc_mod GHC-Options: -Wall + Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 , directory @@ -130,6 +132,7 @@ Executable ghc-modi Main-Is: GHCModi.hs Other-Modules: Paths_ghc_mod GHC-Options: -Wall + Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 , containers @@ -143,12 +146,14 @@ Test-Suite doctest Default-Language: Haskell2010 HS-Source-Dirs: test Ghc-Options: -threaded -Wall + Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs Build-Depends: base , doctest >= 0.9.3 Test-Suite spec Default-Language: Haskell2010 + Extensions: ConstraintKinds, FlexibleContexts Main-Is: Main.hs Hs-Source-Dirs: test, . Type: exitcode-stdio-1.0 diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 2856a87..8424a3d 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -111,7 +111,7 @@ main = flip E.catches handlers $ do nArgs n f = if length remainingArgs == n then f else E.throw (ArgumentsMismatch cmdArg0) - res <- runGhcMod opt $ case cmdArg0 of + res <- runGhcModT opt $ case cmdArg0 of "list" -> modules "lang" -> languages "flag" -> flags @@ -152,7 +152,7 @@ main = flip E.catches handlers $ do hPutStrLn stderr $ "\"" ++ file ++ "\" not found" printUsage printUsage = hPutStrLn stderr $ '\n' : O.usageInfo usage argspec - withFile :: (FilePath -> GhcMod a) -> FilePath -> GhcMod a + withFile :: IOish m => (FilePath -> GhcModT m a) -> FilePath -> GhcModT m a withFile cmd file = do exist <- liftIO $ doesFileExist file if exist diff --git a/src/GHCModi.hs b/src/GHCModi.hs index 39449b4..d67a58d 100644 --- a/src/GHCModi.hs +++ b/src/GHCModi.hs @@ -101,8 +101,8 @@ main = E.handle cmdHandler $ -- c = cradle0 { cradleCurrentDir = rootdir } TODO: ????? setCurrentDirectory rootdir mvar <- liftIO newEmptyMVar - void $ forkIO $ runGhcMod opt $ setupDB mvar - runGhcMod opt $ loop S.empty mvar + void $ forkIO $ runGhcModT opt $ setupDB mvar + runGhcModT opt $ loop S.empty mvar where -- this is just in case. -- If an error is caught here, it is a bug of GhcMod library. @@ -116,7 +116,7 @@ replace (x:xs) = x : replace xs ---------------------------------------------------------------- -setupDB :: MVar SymMdlDb -> GhcMod () +setupDB :: IOish m => MVar SymMdlDb -> GhcModT m () setupDB mvar = ghandle handler $ do liftIO . putMVar mvar =<< getSymMdlDb where @@ -124,7 +124,7 @@ setupDB mvar = ghandle handler $ do ---------------------------------------------------------------- -loop :: Set FilePath -> MVar SymMdlDb -> GhcMod () +loop :: IOish m => Set FilePath -> MVar SymMdlDb -> GhcModT m () loop set mvar = do cmdArg <- liftIO getLine let (cmd,arg') = break (== ' ') cmdArg @@ -152,9 +152,10 @@ loop set mvar = do ---------------------------------------------------------------- -checkStx :: Set FilePath +checkStx :: IOish m + => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) checkStx set file = do set' <- toGhcMod $ newFileSet set file let files = S.toList set' @@ -191,17 +192,17 @@ isSameMainFile file (Just x) ---------------------------------------------------------------- -findSym :: Set FilePath -> String -> MVar SymMdlDb - -> GhcMod (String, Bool, Set FilePath) +findSym :: IOish m => Set FilePath -> String -> MVar SymMdlDb + -> GhcModT m (String, Bool, Set FilePath) findSym set sym mvar = do db <- liftIO $ readMVar mvar opt <- options let ret = lookupSym' opt sym db return (ret, True, set) -lintStx :: Set FilePath +lintStx :: IOish m => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) lintStx set optFile = do ret <- local env' $ lint file return (ret, True, set) @@ -228,36 +229,40 @@ parseLintOptions optFile = case brk (== ']') (dropWhile (/= '[') optFile) of ---------------------------------------------------------------- -showInfo :: Set FilePath +showInfo :: IOish m + => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) showInfo set fileArg = do let [file, expr] = words fileArg set' <- newFileSet set file ret <- info file expr return (ret, True, set') -showType :: Set FilePath +showType :: IOish m + => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) showType set fileArg = do let [file, line, column] = words fileArg set' <- newFileSet set file ret <- types file (read line) (read column) return (ret, True, set') -doSplit :: Set FilePath +doSplit :: IOish m + => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) doSplit set fileArg = do let [file, line, column] = words fileArg set' <- newFileSet set file ret <- splits file (read line) (read column) return (ret, True, set') -doSig :: Set FilePath +doSig :: IOish m + => Set FilePath -> FilePath - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) doSig set fileArg = do let [file, line, column] = words fileArg set' <- newFileSet set file @@ -266,15 +271,17 @@ doSig set fileArg = do ---------------------------------------------------------------- -bootIt :: Set FilePath - -> GhcMod (String, Bool, Set FilePath) +bootIt :: IOish m + => Set FilePath + -> GhcModT m (String, Bool, Set FilePath) bootIt set = do ret <- boot return (ret, True, set) -browseIt :: Set FilePath +browseIt :: IOish m + => Set FilePath -> ModuleString - -> GhcMod (String, Bool, Set FilePath) + -> GhcModT m (String, Bool, Set FilePath) browseIt set mdl = do ret <- browse mdl return (ret, True, set) diff --git a/test/TestUtils.hs b/test/TestUtils.hs index acb6e21..c9ed00f 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -12,14 +12,14 @@ module TestUtils ( import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types -isolateCradle :: GhcMod a -> GhcMod a +isolateCradle :: IOish m => GhcModT m a -> GhcModT m a isolateCradle action = local modifyEnv $ action where modifyEnv e = e { gmCradle = (gmCradle e) { cradlePkgDbStack = [GlobalDb] } } -runIsolatedGhcMod :: Options -> GhcMod a -> IO a -runIsolatedGhcMod opt action = runGhcMod opt $ isolateCradle action +runIsolatedGhcMod :: Options -> GhcModT IO a -> IO a +runIsolatedGhcMod opt action = runGhcModT opt $ isolateCradle action -- | Run GhcMod in isolated cradle with default options runID = runIsolatedGhcMod defaultOptions @@ -28,7 +28,9 @@ runID = runIsolatedGhcMod defaultOptions runI = runIsolatedGhcMod -- | Run GhcMod -run = runGhcMod +run :: Options -> GhcModT IO a -> IO a +run = runGhcModT -- | Run GhcMod with default options -runD = runGhcMod defaultOptions +runD :: GhcModT IO a -> IO a +runD = runGhcModT defaultOptions From 0a62ad911601277a9e93f7fff9d60ba09e881db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Jul 2014 16:54:49 +0200 Subject: [PATCH 20/37] Fix doctest --- test/doctests.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/doctests.hs b/test/doctests.hs index 8bf21ed..b860d45 100644 --- a/test/doctests.hs +++ b/test/doctests.hs @@ -6,6 +6,7 @@ main :: IO () main = doctest [ "-package" , "ghc" + , "-XConstraintKinds", "-XFlexibleContexts" , "-idist/build/autogen/" , "-optP-include" , "-optPdist/build/autogen/cabal_macros.h" From 7474a1b652a73930aca8bf95e96457cee11a3672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 15 Jul 2014 00:51:22 +0200 Subject: [PATCH 21/37] Bring back `GhcMod` but this time it's a GhcModT with an ErrorT inside --- Language/Haskell/GhcMod/Monad.hs | 72 ++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 8b69921..960e5c6 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -1,10 +1,14 @@ {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-} {-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-} {-# LANGUAGE TypeFamilies, UndecidableInstances, RecordWildCards #-} +{-# LANGUAGE StandaloneDeriving #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Haskell.GhcMod.Monad ( - GhcModT + GhcMod + , runGhcMod + , liftGhcMod + , GhcModT , IOish , GhcModEnv(..) , GhcModWriter @@ -16,6 +20,8 @@ module Language.Haskell.GhcMod.Monad ( , toGhcMod , options , cradle + , Options(..) + , defaultOptions , module Control.Monad.Reader.Class , module Control.Monad.Writer.Class , module Control.Monad.State.Class @@ -54,6 +60,7 @@ import Data.Monoid (Monoid) import Control.Applicative (Alternative) import Control.Monad (MonadPlus, liftM, void) import Control.Monad.Base (MonadBase, liftBase) +import Control.Monad.Trans.RWS.Lazy (liftCatch) import Control.Monad.Reader.Class import Control.Monad.State.Class @@ -62,6 +69,7 @@ import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Writer.Class +import Control.Monad.Error import Data.Maybe (fromJust, isJust) import Data.IORef (IORef, readIORef, writeIORef, newIORef) @@ -77,17 +85,29 @@ data GhcModEnv = GhcModEnv { , gmCradle :: Cradle } -data GhcModState = GhcModState +data GhcModState = GhcModState deriving (Eq,Show,Read) defaultState :: GhcModState defaultState = GhcModState type GhcModWriter = () +data GhcModError = GMENoMsg + | GMEString String + | GMECabal + | GMEGhc + deriving (Eq,Show,Read) + +instance Error GhcModError where + noMsg = GMENoMsg + strMsg = GMEString + ---------------------------------------------------------------- type IOish m = (Functor m, MonadIO m, MonadBaseControl IO m) +type GhcMod a = GhcModT (ErrorT GhcModError IO) a + newtype GhcModT m a = GhcModT { unGhcModT :: RWST GhcModEnv GhcModWriter GhcModState m a } deriving (Functor @@ -102,6 +122,8 @@ newtype GhcModT m a = GhcModT { , MonadTrans ) +deriving instance MonadError GhcModError m => MonadError GhcModError (GhcModT m) + #if __GLASGOW_HASKELL__ < 708 instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where -- liftIO :: MonadIO m => IO a -> m a @@ -154,15 +176,6 @@ initSession build Options {..} CompilerOptions {..} = do ---------------------------------------------------------------- -runGhcModT' :: IOish m - => GhcModEnv - -> GhcModState - -> GhcModT m a - -> m (a,(GhcModState, GhcModWriter)) -runGhcModT' r s a = do - (a',s',w) <- runRWST (unGhcModT $ initGhcMonad (Just libdir) >> a) r s - return (a',(s',w)) - newGhcModEnv :: Options -> FilePath -> IO GhcModEnv newGhcModEnv opt dir = do session <- newIORef (error "empty session") @@ -173,6 +186,9 @@ newGhcModEnv opt dir = do , gmCradle = c } +-- | Run a @GhcModT m@ computation, i.e. one with a custom underlying monad. +-- +-- You probably don't want this, look at 'runGhcMod' instead. runGhcModT :: IOish m => Options -> GhcModT m a -> m a runGhcModT opt action = do env <- liftBase $ newGhcModEnv opt =<< getCurrentDirectory @@ -182,6 +198,38 @@ runGhcModT opt action = do initializeFlagsWithCradle opt (gmCradle env) action return a + +-- | Run a computation inside @GhcModT@ providing the RWST environment and +-- initial state. This is a low level function, use it only if you know what to +-- do with 'GhcModEnv' and 'GhcModState'. +-- +-- You should probably look at 'runGhcModT' instead. +runGhcModT' :: IOish m + => GhcModEnv + -> GhcModState + -> GhcModT m a + -> m (a,(GhcModState, GhcModWriter)) +runGhcModT' r s a = do + (a',s',w) <- runRWST (unGhcModT $ initGhcMonad (Just libdir) >> a) r s + return (a',(s',w)) + +-- | Run a 'GhcMod' computation. If you want an underlying monad other than +-- 'ErrorT e IO' you should look at 'runGhcModT' +runGhcMod :: Options + -> GhcMod a + -> IO (Either GhcModError a) +runGhcMod o a = + runErrorT $ runGhcModT o a + +liftErrorT :: IOish m => GhcModT m a -> GhcModT (ErrorT GhcModError m) a +liftErrorT action = + GhcModT $ RWST $ \e s -> ErrorT $ Right <$> (runRWST $ unGhcModT action) e s + +-- | Lift @(GhcModT IO)@ into @GhcMod@, which is an alias for @GhcModT (ErrorT +-- GhcModError IO)@. +liftGhcMod :: GhcModT IO a -> GhcMod a +liftGhcMod = liftErrorT + ---------------------------------------------------------------- withErrorHandler :: IOish m => String -> GhcModT m a -> GhcModT m a @@ -212,7 +260,7 @@ instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where newtype StM (GhcModT m) a = StGhcMod { - unStGhcMod :: StM (RWST GhcModEnv () GhcModState m) a } + unStGhcMod :: StM (RWST GhcModEnv GhcModWriter GhcModState m) a } liftBaseWith f = GhcModT . liftBaseWith $ \runInBase -> f $ liftM StGhcMod . runInBase . unGhcModT From 68212d46a1c29952cf07a324859a602adbcbac48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 15 Jul 2014 01:53:06 +0200 Subject: [PATCH 22/37] Fix cabal file --- ghc-mod.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index f154bcb..e357b51 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -51,7 +51,7 @@ Extra-Source-Files: ChangeLog Library Default-Language: Haskell2010 GHC-Options: -Wall - Extensions: ConstraintKinds, FlexibleContexts + Default-Extensions: ConstraintKinds, FlexibleContexts Exposed-Modules: Language.Haskell.GhcMod Language.Haskell.GhcMod.Ghc Language.Haskell.GhcMod.Monad @@ -118,7 +118,7 @@ Executable ghc-mod Main-Is: GHCMod.hs Other-Modules: Paths_ghc_mod GHC-Options: -Wall - Extensions: ConstraintKinds, FlexibleContexts + Default-Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 , directory @@ -132,7 +132,7 @@ Executable ghc-modi Main-Is: GHCModi.hs Other-Modules: Paths_ghc_mod GHC-Options: -Wall - Extensions: ConstraintKinds, FlexibleContexts + Default-Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 , containers @@ -146,14 +146,14 @@ Test-Suite doctest Default-Language: Haskell2010 HS-Source-Dirs: test Ghc-Options: -threaded -Wall - Extensions: ConstraintKinds, FlexibleContexts + Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs Build-Depends: base , doctest >= 0.9.3 Test-Suite spec Default-Language: Haskell2010 - Extensions: ConstraintKinds, FlexibleContexts + Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: Main.hs Hs-Source-Dirs: test, . Type: exitcode-stdio-1.0 From 686179f12b87387ffb6d1b5fc007516d886e729c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 15 Jul 2014 02:34:07 +0200 Subject: [PATCH 23/37] Fix building with ghc < 7.8 --- Language/Haskell/GhcMod/Monad.hs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 960e5c6..f00bb69 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -27,6 +27,16 @@ module Language.Haskell.GhcMod.Monad ( , module Control.Monad.State.Class ) 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.Cradle import Language.Haskell.GhcMod.DynFlags @@ -51,9 +61,9 @@ import HscTypes -- So, RWST automatically becomes an instance of MonadIO. import MonadUtils -#if __GLASGOW_HASKELL__ < 708 --- To make RWST an instance of MonadIO. +#if DIFFERENT_MONADIO import Control.Monad.Trans.Class (lift) +import qualified Control.Monad.IO.Class import Data.Monoid (Monoid) #endif @@ -65,11 +75,12 @@ import Control.Monad.Trans.RWS.Lazy (liftCatch) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class +import Control.Monad.Trans.Maybe import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Writer.Class -import Control.Monad.Error +import Control.Monad.Error (Error(..), ErrorT(..), MonadError) import Data.Maybe (fromJust, isJust) import Data.IORef (IORef, readIORef, writeIORef, newIORef) @@ -116,6 +127,9 @@ newtype GhcModT m a = GhcModT { , Monad , MonadPlus , MonadIO +#if DIFFERENT_MONADIO + , Control.Monad.IO.Class.MonadIO +#endif , MonadReader GhcModEnv , MonadWriter GhcModWriter , MonadState GhcModState @@ -124,10 +138,16 @@ newtype GhcModT m a = GhcModT { deriving instance MonadError GhcModError m => MonadError GhcModError (GhcModT m) -#if __GLASGOW_HASKELL__ < 708 +#if MONADIO_INSTANCES instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where --- liftIO :: MonadIO m => IO a -> m a liftIO = lift . liftIO + +instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where + liftIO = lift . liftIO + +instance (MonadIO m) => MonadIO (MaybeT m) where + liftIO = lift . liftIO + #endif ---------------------------------------------------------------- From fdfa70e27adea2394647bc5fd0c9875a7cb0b791 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 11:51:52 +0900 Subject: [PATCH 24/37] removing -threaded --- ghc-mod.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 9d32be8..9623b77 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -145,7 +145,7 @@ Test-Suite doctest Type: exitcode-stdio-1.0 Default-Language: Haskell2010 HS-Source-Dirs: test - Ghc-Options: -threaded -Wall + Ghc-Options: -Wall Default-Extensions: ConstraintKinds, FlexibleContexts Main-Is: doctests.hs Build-Depends: base From 755fa41fc0f8ae8ffbc8c626063b8d1557d0501f Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 11:54:39 +0900 Subject: [PATCH 25/37] ver bumps up for ghc.el. --- elisp/ghc.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elisp/ghc.el b/elisp/ghc.el index 4b15f6d..7678cb3 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -28,7 +28,7 @@ (< emacs-minor-version minor))) (error "ghc-mod requires at least Emacs %d.%d" major minor))) -(defconst ghc-version "4.1.0") +(defconst ghc-version "5.0.0") ;; (eval-when-compile ;; (require 'haskell-mode)) From 8495633dc89832d41fcc6493c8fffd957402e9a0 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:06:07 +0900 Subject: [PATCH 26/37] explicitly specifying CompManager to ghcMode. --- Language/Haskell/GhcMod/DynFlags.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index 2589ff2..78927b2 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -5,7 +5,7 @@ import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) import Control.Monad (forM, void) -import GHC (DynFlags(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) +import GHC (DynFlags(..), GhcMode(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) import qualified GHC as G import GhcMonad import GHC.Paths (libdir) @@ -22,7 +22,8 @@ setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () -- HscInterpreted setLinkerOptions :: DynFlags -> DynFlags setLinkerOptions df = df { - ghcLink = LinkInMemory + ghcMode = CompManager + , ghcLink = LinkInMemory , hscTarget = HscInterpreted } From 1524d2a43e32895b8689c2ac78f2cf87ff638de5 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:10:18 +0900 Subject: [PATCH 27/37] removing warnings. --- Language/Haskell/GhcMod/Lint.hs | 1 - Language/Haskell/GhcMod/List.hs | 1 - Language/Haskell/GhcMod/Logger.hs | 1 - Language/Haskell/GhcMod/Monad.hs | 2 -- 4 files changed, 5 deletions(-) diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index cfa915f..934f6d4 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -6,7 +6,6 @@ import CoreMonad (liftIO) import Language.Haskell.GhcMod.Logger (checkErrorPrefix) import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad -import Language.Haskell.GhcMod.Types import Language.Haskell.HLint (hlint) -- | Checking syntax of a target file using hlint. diff --git a/Language/Haskell/GhcMod/List.hs b/Language/Haskell/GhcMod/List.hs index ec93ae3..675e21d 100644 --- a/Language/Haskell/GhcMod/List.hs +++ b/Language/Haskell/GhcMod/List.hs @@ -6,7 +6,6 @@ import Data.List (nub, sort) import qualified GHC as G import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Convert -import Language.Haskell.GhcMod.Types import Packages (pkgIdMap, exposedModules, sourcePackageId, display) import UniqFM (eltsUFM) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 2d7121e..175bdbf 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -21,7 +21,6 @@ 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 (Options(..)) import Outputable (PprStyle, SDoc) import System.FilePath (normalise) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index f00bb69..b031b1d 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -70,12 +70,10 @@ import Data.Monoid (Monoid) import Control.Applicative (Alternative) import Control.Monad (MonadPlus, liftM, void) import Control.Monad.Base (MonadBase, liftBase) -import Control.Monad.Trans.RWS.Lazy (liftCatch) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class -import Control.Monad.Trans.Maybe import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) From be9a67f02a4126b57050a8837b10e27fe616e671 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:29:27 +0900 Subject: [PATCH 28/37] moving #if to Gap.hs. --- Language/Haskell/GhcMod/CabalApi.hs | 23 +++++------------------ Language/Haskell/GhcMod/Gap.hs | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalApi.hs b/Language/Haskell/GhcMod/CabalApi.hs index b052c4d..e312488 100644 --- a/Language/Haskell/GhcMod/CabalApi.hs +++ b/Language/Haskell/GhcMod/CabalApi.hs @@ -10,9 +10,10 @@ module Language.Haskell.GhcMod.CabalApi ( , cabalConfigDependencies ) where -import Language.Haskell.GhcMod.Types -import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.CabalConfig +import Language.Haskell.GhcMod.Gap (benchmarkBuildInfo, benchmarkTargets, toModuleString) +import Language.Haskell.GhcMod.GhcPkg +import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) import qualified Control.Exception as E @@ -20,7 +21,6 @@ import Control.Monad (filterM) import CoreMonad (liftIO) import Data.Maybe (maybeToList) import Data.Set (fromList, toList) -import Distribution.ModuleName (ModuleName,toFilePath) import Distribution.Package (Dependency(Dependency) , PackageName(PackageName)) import qualified Distribution.Package as C @@ -119,11 +119,7 @@ cabalAllBuildInfo pd = libBI ++ execBI ++ testBI ++ benchBI libBI = map P.libBuildInfo $ maybeToList $ P.library pd execBI = map P.buildInfo $ P.executables pd testBI = map P.testBuildInfo $ P.testSuites pd -#if __GLASGOW_HASKELL__ >= 704 - benchBI = map P.benchmarkBuildInfo $ P.benchmarks pd -#else - benchBI = [] -#endif + benchBI = benchmarkBuildInfo pd ---------------------------------------------------------------- @@ -172,16 +168,7 @@ cabalAllTargets pd = do Just l -> P.libModules l libTargets = map toModuleString lib -#if __GLASGOW_HASKELL__ >= 704 - benchTargets = map toModuleString $ concatMap P.benchmarkModules $ P.benchmarks pd -#else - benchTargets = [] -#endif - toModuleString :: ModuleName -> String - toModuleString mn = fromFilePath $ toFilePath mn - - fromFilePath :: FilePath -> String - fromFilePath fp = map (\c -> if c=='/' then '.' else c) fp + benchTargets = benchmarkTargets pd getTestTarget :: TestSuite -> IO [String] getTestTarget ts = diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 63ef445..97b7eb6 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -33,6 +33,9 @@ module Language.Haskell.GhcMod.Gap ( , fileModSummary , WarnFlags , emptyWarnFlags + , benchmarkBuildInfo + , benchmarkTargets + , toModuleString ) where import Control.Applicative hiding (empty) @@ -58,6 +61,7 @@ import StringBuffer import TcType import Var (varType) +import qualified Distribution.PackageDescription as P import qualified InstEnv import qualified Pretty import qualified StringBuffer as SB @@ -80,6 +84,7 @@ import Data.Convertible #if __GLASGOW_HASKELL__ >= 704 import qualified Data.IntSet as I (IntSet, empty) +import qualified Distribution.ModuleName as M (ModuleName,toFilePath) #endif ---------------------------------------------------------------- @@ -398,3 +403,26 @@ type WarnFlags = [WarningFlag] emptyWarnFlags :: WarnFlags emptyWarnFlags = [] #endif + +---------------------------------------------------------------- +---------------------------------------------------------------- + +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 From 84c3cec0f19b4bf3693d5231eb4703ed0aed54c8 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:35:45 +0900 Subject: [PATCH 29/37] moving #if to Gap.hs. --- Language/Haskell/GhcMod/CaseSplit.hs | 14 ++++---------- Language/Haskell/GhcMod/Gap.hs | 10 ++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 706d18d..8dcdb72 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -8,9 +8,8 @@ import Data.List (find, intercalate) import qualified Data.Text as T import qualified Data.Text.IO as T (readFile) import Exception (ghandle, SomeException(..)) -import GHC (GhcMonad, LHsBind, LHsExpr, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) +import GHC (GhcMonad, LHsBind, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G -import Language.Haskell.GhcMod.Gap (HasType(..)) import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils @@ -60,17 +59,12 @@ getSrcSpanTypeForSplit modSum lineNo colNo = do tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p let bs:_ = listifySpans tcs (lineNo, colNo) :: [LHsBind Id] varPat = find isPatternVar $ listifySpans tcs (lineNo, colNo) :: Maybe (LPat Id) - match:_ = listifyParsedSpans pms (lineNo, colNo) -#if __GLASGOW_HASKELL__ < 708 - :: [G.LMatch G.RdrName] -#else - :: [G.LMatch G.RdrName (LHsExpr G.RdrName)] -#endif + match:_ = listifyParsedSpans pms (lineNo, colNo) :: [Gap.GLMatch] case varPat of Nothing -> return Nothing Just varPat' -> do - varT <- getType tcm varPat' -- Finally we get the type of the var - bsT <- getType tcm bs + varT <- Gap.getType tcm varPat' -- Finally we get the type of the var + bsT <- Gap.getType tcm bs case (varT, bsT) of (Just varT', Just (_,bsT')) -> let (L matchL (G.Match _ _ (G.GRHSs rhsLs _))) = match diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 97b7eb6..dfa68da 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -36,6 +36,7 @@ module Language.Haskell.GhcMod.Gap ( , benchmarkBuildInfo , benchmarkTargets , toModuleString + , GLMatch ) where import Control.Applicative hiding (empty) @@ -426,3 +427,12 @@ 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) +#else +type GLMatch = LMatch RdrName +#endif From 86829561abd45d307c99e159452ee5ca6e71525d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:41:10 +0900 Subject: [PATCH 30/37] style only. --- Language/Haskell/GhcMod/CaseSplit.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 8dcdb72..01fe777 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -1,24 +1,24 @@ {-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.CaseSplit ( - splits + splits ) where +import CoreMonad (liftIO) import Data.List (find, intercalate) import qualified Data.Text as T import qualified Data.Text.IO as T (readFile) +import qualified DataCon as Ty import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, LHsBind, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G +import Language.Haskell.GhcMod.Convert import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils -import Language.Haskell.GhcMod.Convert -import CoreMonad (liftIO) import Outputable (PprStyle) -import qualified Type as Ty import qualified TyCon as Ty -import qualified DataCon as Ty +import qualified Type as Ty ---------------------------------------------------------------- -- CASE SPLITTING From a7a02a3f4c95ad75656a18ffbb99f1db1f9a9723 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 12:49:12 +0900 Subject: [PATCH 31/37] Bringing back MaybeT This brings back a warning to be fixed. --- Language/Haskell/GhcMod/Monad.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index b031b1d..eeec1cc 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -74,6 +74,7 @@ import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class +import Control.Monad.Trans.Maybe -- fixme import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) From 0b717487085d9cd3b90b45d79cd2ce8e8187ac3d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 14:44:02 +0900 Subject: [PATCH 32/37] moving #if to Gap.hs --- Language/Haskell/GhcMod/FillSig.hs | 42 +++--------------------------- Language/Haskell/GhcMod/Gap.hs | 23 ++++++++++++++++ 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index fc12384..4698af1 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -18,12 +18,6 @@ import Outputable (PprStyle) import qualified Type as Ty import qualified HsBinds as Ty import qualified Class as Ty -#if __GLASGOW_HASKELL__ >= 706 -import OccName (occName) -#else -import OccName (OccName) -import RdrName (rdrNameOcc) -#endif import qualified Language.Haskell.Exts.Annotated as HE ---------------------------------------------------------------- @@ -79,32 +73,9 @@ getSignature modSum lineNo colNo = do -- We found an instance declaration TypecheckedModule{tm_renamed_source = Just tcs ,tm_checked_module_info = minfo} <- G.typecheckModule p - case listifyRenamedSpans tcs (lineNo, colNo) :: [G.LInstDecl G.Name] of - -- Instance declarations of sort 'instance F (G a)' -#if __GLASGOW_HASKELL__ >= 708 - [L loc (G.ClsInstD (G.ClsInstDecl {G.cid_poly_ty = - (L _ (G.HsForAllTy _ _ _ (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _))))}))] -> -#elif __GLASGOW_HASKELL__ >= 706 - [L loc (G.ClsInstD - (L _ (G.HsForAllTy _ _ _ (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _)))) _ _ _)] -> -#else - [L loc (G.InstDecl - (L _ (G.HsForAllTy _ _ _ (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _)))) _ _ _)] -> -#endif - obtainClassInfo minfo clsName loc - -- Instance declarations of sort 'instance F G' (no variables) -#if __GLASGOW_HASKELL__ >= 708 - [L loc (G.ClsInstD (G.ClsInstDecl {G.cid_poly_ty = - (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _))}))] -> -#elif __GLASGOW_HASKELL__ >= 706 - [L loc (G.ClsInstD - (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _)) _ _ _)] -> -#else - [L loc (G.InstDecl - (L _ (G.HsAppTy (L _ (G.HsTyVar clsName)) _)) _ _ _)] -> -#endif - obtainClassInfo minfo clsName loc - _ -> return Nothing + case Gap.getClass $ listifyRenamedSpans tcs (lineNo, colNo) of + Just (clsName,loc) -> obtainClassInfo minfo clsName loc + Nothing -> return Nothing _ -> return Nothing where obtainClassInfo :: GhcMonad m => G.ModuleInfo -> G.Name -> SrcSpan -> m (Maybe SigInfo) obtainClassInfo minfo clsName loc = do @@ -158,7 +129,7 @@ class FnArgsInfo ty name | ty -> name, name -> ty where getFnArgs :: ty -> [FnArg] instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where - getFnName dflag style name = showOccName dflag style $ occName name + getFnName dflag style name = showOccName dflag style $ Gap.occName name getFnArgs (G.HsForAllTy _ _ _ (L _ iTy)) = 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 @@ -169,11 +140,6 @@ instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where _ -> False getFnArgs _ = [] -#if __GLASGOW_HASKELL__ < 706 -occName :: G.RdrName -> OccName -occName = rdrNameOcc -#endif - instance FnArgsInfo (HE.Type HE.SrcSpanInfo) (HE.Name HE.SrcSpanInfo) where getFnName _ _ (HE.Ident _ s) = s getFnName _ _ (HE.Symbol _ s) = s diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index dfa68da..c1a3a56 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -37,6 +37,8 @@ module Language.Haskell.GhcMod.Gap ( , benchmarkTargets , toModuleString , GLMatch + , getClass + , occName ) where import Control.Applicative hiding (empty) @@ -81,6 +83,7 @@ import GHC hiding (ClsInst) import GHC hiding (Instance) import Control.Arrow hiding ((<+>)) import Data.Convertible +import RdrName (rdrNameOcc) #endif #if __GLASGOW_HASKELL__ >= 704 @@ -436,3 +439,23 @@ type GLMatch = LMatch RdrName (LHsExpr RdrName) #else type GLMatch = LMatch RdrName #endif + +getClass :: [LInstDecl Name] -> Maybe (Name, SrcSpan) +#if __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) +#elif __GLASGOW_HASKELL__ >= 706 +getClass [L loc (ClsInstD (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc) +getClass[L loc (ClsInstD (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc) +#else +getClass [L loc (InstDecl (L _ (HsForAllTy _ _ _ (L _ (HsAppTy (L _ (HsTyVar className)) _)))) _ _ _)] = Just (className, loc) +getClass [L loc (InstDecl (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = Just (className, loc) +#endif +getClass _ = Nothing + +#if __GLASGOW_HASKELL__ < 706 +occName :: G.RdrName -> OccName +occName = rdrNameOcc +#endif From c87ea45488154adce82198b6f46f1c62996ade34 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 14:45:41 +0900 Subject: [PATCH 33/37] adding #if to Monad.hs. --- Language/Haskell/GhcMod/Monad.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index eeec1cc..ebcc7cc 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -74,7 +74,9 @@ import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Reader.Class import Control.Monad.State.Class import Control.Monad.Trans.Class -import Control.Monad.Trans.Maybe -- fixme +#if __GLASGOW_HASKELL__ < 708 +import Control.Monad.Trans.Maybe +#endif import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) From 25730e2a6c76311c40c07e384741bf21d889ada6 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 15:13:06 +0900 Subject: [PATCH 34/37] trying to fix CI --- Language/Haskell/GhcMod/Gap.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index c1a3a56..7bc9181 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -456,6 +456,6 @@ getClass [L loc (InstDecl (L _ (HsAppTy (L _ (HsTyVar className)) _)) _ _ _)] = getClass _ = Nothing #if __GLASGOW_HASKELL__ < 706 -occName :: G.RdrName -> OccName +occName :: RdrName -> OccName occName = rdrNameOcc #endif From 49284a64be8cb8ae7ceb29d76d434ff7422c7c49 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 17:20:35 +0900 Subject: [PATCH 35/37] Uses HscNothing and falls back to HscInterpreted if necessary (#205) Two Test case are fails but it is not fatal. --- Language/Haskell/GhcMod/Check.hs | 3 +- Language/Haskell/GhcMod/DynFlags.hs | 48 ++++++++++++++++++++++++----- Language/Haskell/GhcMod/Logger.hs | 28 +++++++++++------ Language/Haskell/GhcMod/Monad.hs | 2 +- 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/Language/Haskell/GhcMod/Check.hs b/Language/Haskell/GhcMod/Check.hs index 66c7b71..f30a48d 100644 --- a/Language/Haskell/GhcMod/Check.hs +++ b/Language/Haskell/GhcMod/Check.hs @@ -34,8 +34,7 @@ check :: IOish m => [FilePath] -- ^ The target files. -> GhcModT m (Either String String) check fileNames = do - withLogger setAllWaringFlags $ do - setTargetFiles fileNames + withLogger setAllWaringFlags $ setTargetFiles fileNames ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index 78927b2..7cbbd4d 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -4,11 +4,12 @@ import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Types import Control.Applicative ((<$>)) -import Control.Monad (forM, void) +import Control.Monad (forM, void, (>=>)) import GHC (DynFlags(..), GhcMode(..), GhcLink(..), HscTarget(..), LoadHowMuch(..)) import qualified GHC as G import GhcMonad import GHC.Paths (libdir) +import DynFlags (ExtensionFlag(..), xopt) import System.IO.Unsafe (unsafePerformIO) @@ -17,11 +18,23 @@ data Build = CabalPkg | SingleFile deriving Eq setEmptyLogger :: DynFlags -> DynFlags setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () --- we don't want to generate object code so we compile to bytecode --- (HscInterpreted) which implies LinkInMemory --- HscInterpreted -setLinkerOptions :: DynFlags -> DynFlags -setLinkerOptions df = df { +-- Fast +-- Friendly to foreign export +-- Not friendly to Template Haskell +-- Uses small memory +setModeSimple :: DynFlags -> DynFlags +setModeSimple df = df { + ghcMode = CompManager + , ghcLink = NoLink + , hscTarget = HscNothing + } + +-- Slow +-- Not friendly to foreign export +-- Friendly to Template Haskell +-- Uses large memory +setModeIntelligent :: DynFlags -> DynFlags +setModeIntelligent df = df { ghcMode = CompManager , ghcLink = LinkInMemory , hscTarget = HscInterpreted @@ -59,7 +72,28 @@ setTargetFiles :: (GhcMonad m) => [FilePath] -> m () setTargetFiles files = do targets <- forM files $ \file -> G.guessTarget file Nothing G.setTargets targets - void $ G.load LoadAllTargets + xs <- G.depanal [] False + -- FIXME, checking state + loadTargets $ needsFallback xs + where + loadTargets False = 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 True = do + df <- G.getSessionDynFlags + void $ G.setSessionDynFlags (setModeIntelligent df) + void $ G.load LoadAllTargets + +needsFallback :: G.ModuleGraph -> Bool +needsFallback = any (hasTHorQQ . G.ms_hspp_opts) + where + hasTHorQQ :: DynFlags -> Bool + hasTHorQQ dflags = any (`xopt` dflags) [Opt_TemplateHaskell, Opt_QuasiQuotes] ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 175bdbf..6f9ce6c 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -6,7 +6,7 @@ module Language.Haskell.GhcMod.Logger ( ) where import Bag (Bag, bagToList) -import Control.Applicative ((<$>),(*>)) +import Control.Applicative ((<$>)) import CoreMonad (liftIO) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) import Data.List (isPrefixOf) @@ -28,21 +28,29 @@ import System.FilePath (normalise) type Builder = [String] -> [String] -newtype LogRef = LogRef (IORef Builder) +data Log = Log [String] Builder + +newtype LogRef = LogRef (IORef Log) + +emptyLog :: Log +emptyLog = Log [] id newLogRef :: IO LogRef -newLogRef = LogRef <$> newIORef id +newLogRef = LogRef <$> newIORef emptyLog readAndClearLogRef :: IOish m => LogRef -> GhcModT m String readAndClearLogRef (LogRef ref) = do - b <- liftIO $ readIORef ref - liftIO $ writeIORef ref id + Log _ b <- liftIO $ readIORef ref + liftIO $ writeIORef ref emptyLog convert' (b []) appendLogRef :: DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () -appendLogRef df (LogRef ref) _ sev src style msg = do - let !l = ppMsg src sev df style msg - modifyIORef ref (\b -> b . (l:)) +appendLogRef df (LogRef ref) _ sev src style msg = modifyIORef ref update + where + l = ppMsg src sev df style msg + update lg@(Log ls b) + | l `elem` ls = lg + | otherwise = Log (l:ls) (b . (l:)) ---------------------------------------------------------------- @@ -57,7 +65,9 @@ withLogger setDF body = ghandle sourceError $ do logref <- liftIO $ newLogRef wflags <- filter ("-fno-warn" `isPrefixOf`) . ghcOpts <$> options withDynFlags (setLogger logref . setDF) $ do - withCmdFlags wflags $ do body *> (Right <$> readAndClearLogRef logref) + withCmdFlags wflags $ do + body + Right <$> readAndClearLogRef logref where setLogger logref df = Gap.setLogAction df $ appendLogRef df logref diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index ebcc7cc..02e9df4 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -189,7 +189,7 @@ initSession :: GhcMonad m initSession build Options {..} CompilerOptions {..} = do df <- G.getSessionDynFlags void $ G.setSessionDynFlags =<< (addCmdOpts ghcOptions - $ setLinkerOptions + $ setModeSimple $ setIncludeDirs includeDirs $ setBuildEnv build $ setEmptyLogger From 204d449aefd5beb017109974c83bb009f6e62b6b Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 21:34:05 +0900 Subject: [PATCH 36/37] set optimization level to 0 just in case. --- Language/Haskell/GhcMod/DynFlags.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index 7cbbd4d..76b6da1 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -27,6 +27,7 @@ setModeSimple df = df { ghcMode = CompManager , ghcLink = NoLink , hscTarget = HscNothing + , optLevel = 0 } -- Slow @@ -38,6 +39,7 @@ setModeIntelligent df = df { ghcMode = CompManager , ghcLink = LinkInMemory , hscTarget = HscInterpreted + , optLevel = 0 } setIncludeDirs :: [IncludeDir] -> DynFlags -> DynFlags From 3050ba1863ed3572dd1ce3063c25b863eb693684 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Jul 2014 21:40:22 +0900 Subject: [PATCH 37/37] fixing tests. --- test/data/Info.hs | 2 ++ test/data/Mutual1.hs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/data/Info.hs b/test/data/Info.hs index 7370abc..4228f64 100644 --- a/test/data/Info.hs +++ b/test/data/Info.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted + module Info () where fib :: Int -> Int diff --git a/test/data/Mutual1.hs b/test/data/Mutual1.hs index ef23310..1b73625 100644 --- a/test/data/Mutual1.hs +++ b/test/data/Mutual1.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE TemplateHaskell #-} -- for HscInterpreted + module Mutual1 where import Mutual2