From e252c7b76285b10ccc15571c97e0082325214f4d Mon Sep 17 00:00:00 2001 From: Bodigrim Date: Fri, 24 Jul 2015 00:07:56 +0300 Subject: [PATCH 001/147] Fix typo in help --- src/GHCMod.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index ba9b79d..b8e2b1c 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -163,7 +163,7 @@ usage = \ - lint FILE\n\ \ Check files using `hlint'.\n\ \ Flags:\n\ - \ -l\n\ + \ -h\n\ \ Option to be passed to hlint.\n\ \\n\ \ - root\n\ From f8fe76ce5d6f728aefbd0aca37e99f17de94442c Mon Sep 17 00:00:00 2001 From: Rolf Karp Date: Sat, 15 Aug 2015 19:06:28 +0200 Subject: [PATCH 002/147] Add Stack build configuration --- .gitignore | 1 + stack.yaml | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 stack.yaml diff --git a/.gitignore b/.gitignore index f280993..05fd776 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ dist/ elisp/*.elc *~ /.cabal-sandbox/ +/.stack-work/ add-source-timestamps package.cache cabal.sandbox.config diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 0000000..efce96e --- /dev/null +++ b/stack.yaml @@ -0,0 +1,6 @@ +flags: {} +packages: +- '.' +extra-deps: +- cabal-helper-0.5.1.0 +resolver: lts-3.0 From 3790fca20bd264419acb3180de3c7369f70cb035 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 31 May 2015 11:32:46 +0300 Subject: [PATCH 003/147] Initial support for file redirection Rewrite, taking discussion into consideration --- Language/Haskell/GhcMod.hs | 4 ++ Language/Haskell/GhcMod/FileMapping.hs | 62 ++++++++++++++++++++++ Language/Haskell/GhcMod/HomeModuleGraph.hs | 9 ++-- Language/Haskell/GhcMod/Internal.hs | 3 ++ Language/Haskell/GhcMod/Monad/Types.hs | 30 +++++++++++ Language/Haskell/GhcMod/Target.hs | 38 ++++++++----- Language/Haskell/GhcMod/Types.hs | 11 +++- ghc-mod.cabal | 1 + src/GHCMod.hs | 16 ++++++ 9 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 Language/Haskell/GhcMod/FileMapping.hs diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index d1eecd8..d17d0e8 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -9,6 +9,7 @@ module Language.Haskell.GhcMod ( , Options(..) , LineSeparator(..) , OutputStyle(..) + , FileMapping(..) , defaultOptions -- * Logging , GmLogLevel @@ -63,6 +64,9 @@ module Language.Haskell.GhcMod ( , gmErrStrLn , gmUnsafePutStrLn , gmUnsafeErrStrLn + -- * FileMapping + , getMMappedFiles + , setMMappedFiles ) where import Language.Haskell.GhcMod.Boot diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs new file mode 100644 index 0000000..f4602e6 --- /dev/null +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -0,0 +1,62 @@ +module Language.Haskell.GhcMod.FileMapping + ( loadMappedFile + , loadMappedFiles + , delMMappedFile + , mapFile + ) where + +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types +import Language.Haskell.GhcMod.Gap +import Language.Haskell.GhcMod.HomeModuleGraph + +import System.Directory +import System.FilePath + +import Data.Time + +import GHC + +loadMappedFiles :: IOish m => GhcModT m () +loadMappedFiles = do + Options {fileMappings} <- options + mapM_ (uncurry loadMappedFile) $ reverse fileMappings + +loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () +loadMappedFile from fm@(RedirectedMapping _) = + addToState from fm +loadMappedFile from (MemoryMapping _) = do + let loop' acc = do + line <- getLine + if not (null line) && last line == '\EOT' + then return $ acc ++ init line + else loop' (acc++line++"\n") + src <- liftIO $ loop' "" + addToState from (MemoryMapping $ Just src) + +addToState :: IOish m => FilePath -> FileMapping -> GhcModT m () +addToState from fm = do + crdl <- cradle + let ccfn = cradleCurrentDir crdl from + cfn <- liftIO $ canonicalizePath ccfn + addMMappedFile cfn fm + +mapFile :: (IOish m, GmState m, GhcMonad m) => + HscEnv -> Target -> m Target +mapFile _ (Target tid@(TargetFile filePath _) taoc _) = do + mapping <- lookupMMappedFile filePath + mkMappedTarget tid taoc mapping +mapFile env (Target tid@(TargetModule moduleName) taoc _) = do + filePath <- liftIO $ findModulePath env moduleName + mapping <- maybe (return Nothing) lookupMMappedFile $ fmap mpPath filePath + mkMappedTarget tid taoc mapping + +mkMappedTarget :: (IOish m, GmState m, GhcMonad m) => + TargetId -> Bool -> Maybe FileMapping -> m Target +mkMappedTarget _ taoc (Just (RedirectedMapping to)) = + return $ mkTarget (TargetFile to Nothing) taoc Nothing +mkMappedTarget tid taoc (Just (MemoryMapping (Just src))) = do + sb <- toStringBuffer [src] + ct <- liftIO getCurrentTime + return $ mkTarget tid taoc $ Just (sb, ct) +mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index d10f483..6000a2e 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -62,6 +62,8 @@ import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Gap (parseModuleHeader) +import System.IO + -- | Turn module graph into a graphviz dot file -- -- @dot -Tpng -o modules.png modules.dot@ @@ -124,7 +126,7 @@ pruneUnreachable smp0 gmg@GmModuleGraph {..} = let collapseMaybeSet :: Maybe (Set a) -> Set a collapseMaybeSet = maybe Set.empty id -homeModuleGraph :: (IOish m, GmLog m, GmEnv m) +homeModuleGraph :: (IOish m, GmLog m, GmEnv m, GmState m) => HscEnv -> Set ModulePath -> m GmModuleGraph homeModuleGraph env smp = updateHomeModuleGraph env mempty smp smp @@ -159,7 +161,7 @@ canonicalizeModuleGraph GmModuleGraph {..} = liftIO $ do fmg (mp, smp) = liftM2 (,) (canonicalizeModulePath mp) (Set.fromList <$> mapM canonicalizeModulePath (Set.toList smp)) -updateHomeModuleGraph :: (IOish m, GmLog m, GmEnv m) +updateHomeModuleGraph :: (IOish m, GmLog m, GmEnv m, GmState m) => HscEnv -> GmModuleGraph -> Set ModulePath -- ^ Initial set of modules @@ -185,7 +187,7 @@ mkModuleMap :: Set ModulePath -> Map ModuleName ModulePath mkModuleMap smp = Map.fromList $ map (mpModule &&& id) $ Set.toList smp updateHomeModuleGraph' - :: forall m. (MonadState S m, IOish m, GmLog m, GmEnv m) + :: forall m. (MonadState S m, IOish m, GmLog m, GmEnv m, GmState m) => HscEnv -> Set ModulePath -- ^ Initial set of modules -> m () @@ -224,6 +226,7 @@ updateHomeModuleGraph' env smp0 = do gmLog GmWarning ("preprocess " ++ show fn) $ Monoid.mempty $+$ (vcat $ map text errs) return Nothing + imports :: ModulePath -> String -> DynFlags -> MaybeT m (Set ModulePath) imports mp@ModulePath {..} src dflags = case parseModuleHeader src dflags mpPath of diff --git a/Language/Haskell/GhcMod/Internal.hs b/Language/Haskell/GhcMod/Internal.hs index ea480c8..d5fdff7 100644 --- a/Language/Haskell/GhcMod/Internal.hs +++ b/Language/Haskell/GhcMod/Internal.hs @@ -56,6 +56,8 @@ module Language.Haskell.GhcMod.Internal ( -- * Misc stuff , GHandler(..) , gcatches + -- * FileMapping + , module Language.Haskell.GhcMod.FileMapping ) where import GHC.Paths (libdir) @@ -70,6 +72,7 @@ import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils import Language.Haskell.GhcMod.World import Language.Haskell.GhcMod.CabalHelper +import Language.Haskell.GhcMod.FileMapping -- | Obtaining the directory for ghc system libraries. ghcLibDir :: FilePath diff --git a/Language/Haskell/GhcMod/Monad/Types.hs b/Language/Haskell/GhcMod/Monad/Types.hs index cfcb29b..7a866f3 100644 --- a/Language/Haskell/GhcMod/Monad/Types.hs +++ b/Language/Haskell/GhcMod/Monad/Types.hs @@ -48,6 +48,11 @@ module Language.Haskell.GhcMod.Monad.Types ( , withOptions , getCompilerMode , setCompilerMode + , getMMappedFiles + , setMMappedFiles + , addMMappedFile + , delMMappedFile + , lookupMMappedFile -- * Re-exporting convenient stuff , MonadIO , liftIO @@ -99,6 +104,8 @@ import qualified Control.Monad.IO.Class as MTL import Data.Monoid (Monoid) #endif +import Data.Map (Map, empty) +import qualified Data.Map as M import Data.Maybe import Data.Monoid import Data.IORef @@ -228,6 +235,11 @@ class Monad m => GmState m where return a {-# MINIMAL gmsState | gmsGet, gmsPut #-} +instance GmState m => GmState (StateT s m) where + gmsGet = lift gmsGet + gmsPut = lift . gmsPut + gmsState = lift . gmsState + instance Monad m => GmState (StateT GhcModState m) where gmsGet = get gmsPut = put @@ -434,6 +446,24 @@ getCompilerMode = gmCompilerMode `liftM` gmsGet setCompilerMode :: GmState m => CompilerMode -> m () setCompilerMode mode = (\s -> gmsPut s { gmCompilerMode = mode } ) =<< gmsGet +getMMappedFiles :: GmState m => m FileMappingMap +getMMappedFiles = gmMMappedFiles `liftM` gmsGet + +setMMappedFiles :: GmState m => FileMappingMap -> m () +setMMappedFiles mf = (\s -> gmsPut s { gmMMappedFiles = mf } ) =<< gmsGet + +addMMappedFile :: GmState m => FilePath -> FileMapping -> m () +addMMappedFile t fm = + getMMappedFiles >>= setMMappedFiles . M.insert t fm + +delMMappedFile :: GmState m => FilePath -> m () +delMMappedFile t = + getMMappedFiles >>= setMMappedFiles . M.delete t + +lookupMMappedFile :: GmState m => FilePath -> m (Maybe FileMapping) +lookupMMappedFile t = + M.lookup t `liftM` getMMappedFiles + withOptions :: GmEnv m => (Options -> Options) -> m a -> m a withOptions changeOpt action = gmeLocal changeEnv action where diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index c02d38e..386cec4 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -39,7 +39,7 @@ import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils as U - +import Language.Haskell.GhcMod.FileMapping import Data.Maybe import Data.Monoid as Monoid @@ -163,11 +163,20 @@ runGmlTWith efnmns' mdf wrapper action = do initSession opts' $ setModeSimple >>> setEmptyLogger >>> mdf - let rfns = map (makeRelative $ cradleRootDir crdl) cfns - unGmlT $ wrapper $ do - loadTargets (map moduleNameString mns ++ rfns) + targets <- + withLightHscEnv opts $ \env -> + mapM (`guessTarget` Nothing) (map moduleNameString mns ++ cfns) + >>= mapM (mapFile env) + >>= mapM relativize + loadTargets targets action + where + relativize (Target (TargetFile filePath phase) taoc src) = do + crdl <- cradle + let tid = makeRelative (cradleRootDir crdl) filePath `TargetFile` phase + return $ Target tid taoc src + relativize tgt = return tgt targetGhcOptions :: forall m. IOish m => Cradle @@ -310,7 +319,7 @@ sandboxOpts crdl = do getSandboxPackageDbStack cdir = ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb cdir -resolveGmComponent :: (IOish m, GmLog m, GmEnv m) +resolveGmComponent :: (IOish m, GmLog m, GmEnv m, GmState m) => Maybe [CompilationUnit] -- ^ Updated modules -> GmComponent 'GMCRaw (Set ModulePath) -> m (GmComponent 'GMCResolved (Set ModulePath)) @@ -335,7 +344,7 @@ resolveGmComponent mums c@GmComponent {..} = do [ "-optP-include", "-optP" ++ macrosHeaderPath ] ] -resolveEntrypoint :: (IOish m, GmEnv m, GmLog m) +resolveEntrypoint :: (IOish m, GmEnv m, GmLog m, GmState m) => Cradle -> GmComponent 'GMCRaw ChEntrypoint -> m (GmComponent 'GMCRaw (Set ModulePath)) @@ -367,7 +376,8 @@ resolveChEntrypoints srcDir ChSetupEntrypoint = do chModToMod :: ChModuleName -> ModuleName chModToMod (ChModuleName mn) = mkModuleName mn -resolveModule :: (MonadIO m, GmEnv m, GmLog m) => + +resolveModule :: (MonadIO m, GmEnv m, GmLog m, GmState m) => HscEnv -> [FilePath] -> CompilationUnit -> m (Maybe ModulePath) resolveModule env _srcDirs (Right mn) = liftIO $ traverse canonicalizeModulePath =<< findModulePath env mn @@ -427,12 +437,11 @@ resolveGmComponents mumns cs = do same f a b = (f a) == (f b) -- | Set the files as targets and load them. -loadTargets :: IOish m => [String] -> GmlT m () -loadTargets filesOrModules = do +loadTargets :: IOish m => [Target] -> GmlT m () +loadTargets targets = do gmLog GmDebug "loadTargets" $ - text "Loading" <+>: fsep (map text filesOrModules) + text "Loading" <+>: fsep (map (text . showTargetId) targets) - targets <- forM filesOrModules (flip guessTarget Nothing) setTargets targets mode <- getCompilerMode @@ -459,16 +468,19 @@ loadTargets filesOrModules = do void $ setSessionDynFlags (setModeIntelligent df) void $ load LoadAllTargets - resetTargets targets = do + resetTargets targets' = do setTargets [] void $ load LoadAllTargets - setTargets targets + setTargets targets' setIntelligent = do newdf <- setModeIntelligent <$> getSessionDynFlags void $ setSessionDynFlags newdf setCompilerMode Intelligent + showTargetId (Target (TargetModule s) _ _) = moduleNameString s + showTargetId (Target (TargetFile s _) _ _) = s + needsFallback :: ModuleGraph -> Bool needsFallback = any $ \ms -> let df = ms_hspp_opts ms in diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 859086c..029cd23 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -69,6 +69,12 @@ data OutputStyle = LispStyle -- ^ S expression style. -- | The type for line separator. Historically, a Null string is used. newtype LineSeparator = LineSeparator String deriving (Show) +data FileMapping = RedirectedMapping FilePath + | MemoryMapping (Maybe String) + deriving Show + +type FileMappingMap = Map FilePath FileMapping + data Options = Options { outputStyle :: OutputStyle -- | Line separator string. @@ -93,6 +99,7 @@ data Options = Options { -- | If 'True', 'browse' will return fully qualified name , qualified :: Bool , hlintOpts :: [String] + , fileMappings :: [(FilePath,FileMapping)] } deriving (Show) -- | A default 'Options'. @@ -110,6 +117,7 @@ defaultOptions = Options { , detailed = False , qualified = False , hlintOpts = [] + , fileMappings = [] } ---------------------------------------------------------------- @@ -182,13 +190,14 @@ data GhcModState = GhcModState { , gmComponents :: !(Map ChComponentName (GmComponent 'GMCResolved (Set ModulePath))) , gmCompilerMode :: !CompilerMode , gmCaches :: !GhcModCaches + , gmMMappedFiles :: !FileMappingMap } data CompilerMode = Simple | Intelligent deriving (Eq,Show,Read) defaultGhcModState :: GhcModState defaultGhcModState = - GhcModState n Map.empty Simple (GhcModCaches n n n n) + GhcModState n Map.empty Simple (GhcModCaches n n n n) Map.empty where n = Nothing ---------------------------------------------------------------- diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 40cdde3..8336665 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -105,6 +105,7 @@ Library Language.Haskell.GhcMod.Doc Language.Haskell.GhcMod.DynFlags Language.Haskell.GhcMod.Error + Language.Haskell.GhcMod.FileMapping Language.Haskell.GhcMod.FillSig Language.Haskell.GhcMod.Find Language.Haskell.GhcMod.Flag diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 46d858d..c0f2167 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -5,6 +5,7 @@ module Main where import Config (cProjectVersion) import MonadUtils (liftIO) import Control.Applicative +import Control.Arrow import Control.Monad import Data.Typeable (Typeable) import Data.Version (showVersion) @@ -270,6 +271,14 @@ globalArgSpec = reqArg "OPT" $ \g o -> Right $ o { ghcUserOptions = g : ghcUserOptions o } + , option "" ["file-map"] "Redirect one file to another, --file-map \"file1.hs=file2.hs\"" $ + reqArg "OPT" $ \g o -> + let m = case second (drop 1) $ span (/='=') g of + (s,"") -> (s, MemoryMapping Nothing) + (f,t) -> (f, RedirectedMapping t) + in + Right $ o { fileMappings = m : fileMappings o } + , option "" ["with-ghc"] "GHC executable to use" $ reqArg "PROG" $ \p o -> Right $ o { ghcProgram = p } @@ -429,6 +438,12 @@ legacyInteractiveLoop symdbreq world = do "boot" -> bootCmd [] "browse" -> browseCmd args + "load" -> loadMappedFile arg (MemoryMapping Nothing) + >> return "" + + "unload" -> delMMappedFile arg + >> return "" + "quit" -> liftIO $ exitSuccess "" -> liftIO $ exitSuccess _ -> fatalError $ "unknown command: `" ++ cmd ++ "'" @@ -444,6 +459,7 @@ legacyInteractiveLoop symdbreq world = do ghcCommands :: IOish m => [String] -> GhcModT m () ghcCommands [] = fatalError "No command given (try --help)" ghcCommands (cmd:args) = do + loadMappedFiles gmPutStr =<< action args where action = case cmd of From e72d72eb867a436b94d767fbb46814b9aca0e162 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 9 Jun 2015 12:45:27 +0300 Subject: [PATCH 004/147] Workaround for GHC 7.4 bugs --- Language/Haskell/GhcMod/FileMapping.hs | 5 ++++- src/GHCMod.hs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index f4602e6..fffe350 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -1,7 +1,7 @@ module Language.Haskell.GhcMod.FileMapping ( loadMappedFile , loadMappedFiles - , delMMappedFile + , unloadMappedFile , mapFile ) where @@ -60,3 +60,6 @@ mkMappedTarget tid taoc (Just (MemoryMapping (Just src))) = do ct <- liftIO getCurrentTime return $ mkTarget tid taoc $ Just (sb, ct) mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing + +unloadMappedFile :: IOish m => FilePath -> GhcModT m () +unloadMappedFile = delMMappedFile diff --git a/src/GHCMod.hs b/src/GHCMod.hs index c0f2167..3c512bf 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -441,7 +441,7 @@ legacyInteractiveLoop symdbreq world = do "load" -> loadMappedFile arg (MemoryMapping Nothing) >> return "" - "unload" -> delMMappedFile arg + "unload" -> unloadMappedFile arg >> return "" "quit" -> liftIO $ exitSuccess From e70988e15f93edbb39fc8f8776a80e765574b0b4 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 16 Jun 2015 11:28:14 +0300 Subject: [PATCH 005/147] Use `MaybeT` instead of `maybe (return Nothing)` --- Language/Haskell/GhcMod/FileMapping.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index fffe350..d40bd64 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -15,6 +15,7 @@ import System.FilePath import Data.Time +import Control.Monad.Trans.Maybe import GHC loadMappedFiles :: IOish m => GhcModT m () @@ -47,8 +48,9 @@ mapFile _ (Target tid@(TargetFile filePath _) taoc _) = do mapping <- lookupMMappedFile filePath mkMappedTarget tid taoc mapping mapFile env (Target tid@(TargetModule moduleName) taoc _) = do - filePath <- liftIO $ findModulePath env moduleName - mapping <- maybe (return Nothing) lookupMMappedFile $ fmap mpPath filePath + mapping <- runMaybeT $ do + filePath <- MaybeT $ liftIO $ findModulePath env moduleName + MaybeT $ lookupMMappedFile $ mpPath filePath mkMappedTarget tid taoc mapping mkMappedTarget :: (IOish m, GmState m, GhcMonad m) => From f8a0325617e7659b8b6407e2e43143452cadb45d Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 16 Jun 2015 11:28:46 +0300 Subject: [PATCH 006/147] Load all mapped targets --- Language/Haskell/GhcMod/Monad/Types.hs | 4 ++++ Language/Haskell/GhcMod/Target.hs | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad/Types.hs b/Language/Haskell/GhcMod/Monad/Types.hs index 7a866f3..1339513 100644 --- a/Language/Haskell/GhcMod/Monad/Types.hs +++ b/Language/Haskell/GhcMod/Monad/Types.hs @@ -53,6 +53,7 @@ module Language.Haskell.GhcMod.Monad.Types ( , addMMappedFile , delMMappedFile , lookupMMappedFile + , getMMappedFilePaths -- * Re-exporting convenient stuff , MonadIO , liftIO @@ -464,6 +465,9 @@ lookupMMappedFile :: GmState m => FilePath -> m (Maybe FileMapping) lookupMMappedFile t = M.lookup t `liftM` getMMappedFiles +getMMappedFilePaths :: GmState m => m [FilePath] +getMMappedFilePaths = M.keys `liftM` getMMappedFiles + withOptions :: GmEnv m => (Options -> Options) -> m a -> m a withOptions changeOpt action = gmeLocal changeEnv action where diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 386cec4..67432cc 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -53,6 +53,8 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) import qualified Data.Set as Set +import Data.List (nubBy) +import Data.Function (on) import Distribution.Helper import Prelude hiding ((.)) @@ -163,12 +165,15 @@ runGmlTWith efnmns' mdf wrapper action = do initSession opts' $ setModeSimple >>> setEmptyLogger >>> mdf + mappedStrs <- getMMappedFilePaths + let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns + unGmlT $ wrapper $ do targets <- withLightHscEnv opts $ \env -> - mapM (`guessTarget` Nothing) (map moduleNameString mns ++ cfns) - >>= mapM (mapFile env) - >>= mapM relativize + liftM (nubBy ((==) `on` targetId)) + (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs) + >>= mapM relativize loadTargets targets action where From 2504f643e9a9cd9356960792bcb928ff03b4a994 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 16 Jun 2015 13:49:53 +0300 Subject: [PATCH 007/147] Replace mapped names with original ones in output --- Language/Haskell/GhcMod/HomeModuleGraph.hs | 15 +-- Language/Haskell/GhcMod/Logger.hs | 117 +++++++++++++-------- Language/Haskell/GhcMod/Target.hs | 4 +- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index 6000a2e..ce6fcc2 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -242,21 +242,22 @@ updateHomeModuleGraph' env smp0 = do $ map unLoc hsmodImports liftIO $ Set.fromList . catMaybes <$> mapM (findModulePath env) mns -preprocessFile :: MonadIO m => +preprocessFile :: (IOish m, GmEnv m, GmState m) => HscEnv -> FilePath -> m (Either [String] ([String], (DynFlags, FilePath))) preprocessFile env file = - liftIO $ withLogger' env $ \setDf -> do + withLogger' env $ \setDf -> do let env' = env { hsc_dflags = setDf (hsc_dflags env) } - preprocess env' (file, Nothing) + liftIO $ preprocess env' (file, Nothing) -fileModuleName :: - HscEnv -> FilePath -> IO (Either [String] (Maybe ModuleName)) -fileModuleName env fn = handle (\(_ :: SomeException) -> return $ Right Nothing) $ do +fileModuleName :: (IOish m, GmEnv m, GmState m) => + HscEnv -> FilePath -> m (Either [String] (Maybe ModuleName)) +fileModuleName env fn = do + let handler = liftIO . handle (\(_ :: SomeException) -> return $ Right Nothing) ep <- preprocessFile env fn case ep of Left errs -> do return $ Left errs - Right (_warns, (dflags, procdFile)) -> do + Right (_warns, (dflags, procdFile)) -> handler $ do src <- readFile procdFile case parseModuleHeader src dflags procdFile of Left errs -> do diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 3fbd436..46ab639 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -9,9 +9,11 @@ module Language.Haskell.GhcMod.Logger ( import Control.Arrow import Control.Applicative import Data.List (isPrefixOf) -import Data.Maybe (fromMaybe) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe, mapMaybe) +import Control.Monad.Reader (Reader, asks, runReader) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) -import System.FilePath (normalise) +import System.FilePath (normalise, makeRelative) import Text.PrettyPrint import ErrUtils (ErrMsg, errMsgShortDoc, errMsgExtraInfo) @@ -26,6 +28,7 @@ import Language.Haskell.GhcMod.Doc (showPage) import Language.Haskell.GhcMod.DynFlags (withDynFlags) import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Error +import Language.Haskell.GhcMod.Types import qualified Language.Haskell.GhcMod.Gap as Gap import Prelude @@ -35,6 +38,13 @@ data Log = Log [String] Builder newtype LogRef = LogRef (IORef Log) +data ReaderState = ReaderState { rsDynFlags :: DynFlags + , rsPprStyle :: PprStyle + , rsMapFile :: FilePath -> FilePath + } + +type ReaderM a = Reader ReaderState a + emptyLog :: Log emptyLog = Log [] id @@ -47,10 +57,10 @@ readAndClearLogRef (LogRef ref) = do writeIORef ref emptyLog return $ b [] -appendLogRef :: DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () -appendLogRef df (LogRef ref) _ sev src st msg = modifyIORef ref update +appendLogRef :: ReaderState -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () +appendLogRef rs df (LogRef ref) _ sev src st msg = modifyIORef ref update where - l = ppMsg src sev df st msg + l = runReader (ppMsg src sev msg) rs{rsDynFlags=df, rsPprStyle=st} update lg@(Log ls b) | l `elem` ls = lg | otherwise = Log (l:ls) (b . (l:)) @@ -60,7 +70,7 @@ appendLogRef df (LogRef ref) _ sev src st msg = modifyIORef ref update -- | 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 :: (GmGhc m, GmEnv m) +withLogger :: (GmGhc m, GmEnv m, GmState m) => (DynFlags -> DynFlags) -> m a -> m (Either String (String, a)) @@ -72,74 +82,97 @@ withLogger f action = do withDynFlags (f . setDf) action return $ either (Left . conv) (Right . first conv) eres -withLogger' :: IOish m +withLogger' :: (IOish m, GmState m, GmEnv m) => HscEnv -> ((DynFlags -> DynFlags) -> m a) -> m (Either [String] ([String], a)) withLogger' env action = do logref <- liftIO $ newLogRef + rfm <- do + mm <- Map.toList <$> getMMappedFiles + let + mf :: FilePath -> FileMapping -> Maybe (FilePath, FilePath) + mf from (RedirectedMapping to) + = Just (to, from) + mf _ _ = Nothing + return $ Map.fromList $ mapMaybe (uncurry mf) mm + + crdl <- cradle + let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) - st = mkUserStyle pu AllTheWay + stl = mkUserStyle pu AllTheWay + st = ReaderState { + rsDynFlags = dflags + , rsPprStyle = stl + , rsMapFile = \key -> + fromMaybe key + $ makeRelative (cradleRootDir crdl) + <$> Map.lookup key rfm + } - fn df = setLogger logref df + setLogger df = Gap.setLogAction df $ appendLogRef st df logref + handlers = [ + GHandler $ \ex -> return $ Left $ runReader (sourceError ex) st, + GHandler $ \ex -> return $ Left [render $ ghcExceptionDoc ex] + ] - a <- gcatches (Right <$> action fn) (handlers dflags st) + a <- gcatches (Right <$> action setLogger) handlers ls <- liftIO $ readAndClearLogRef logref - return $ ((,) ls <$> a) - - where - setLogger logref df = Gap.setLogAction df $ appendLogRef df logref - handlers df st = [ - GHandler $ \ex -> return $ Left $ sourceError df st ex, - GHandler $ \ex -> return $ Left [render $ ghcExceptionDoc ex] - ] + return ((,) ls <$> a) errBagToStrList :: HscEnv -> Bag ErrMsg -> [String] errBagToStrList env errs = let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) st = mkUserStyle pu AllTheWay - in errsToStr dflags st $ bagToList errs + in runReader (errsToStr (bagToList errs)) ReaderState{rsDynFlags=dflags, rsPprStyle=st} ---------------------------------------------------------------- -- | Converting 'SourceError' to 'String'. -sourceError :: DynFlags -> PprStyle -> SourceError -> [String] -sourceError df st src_err = errsToStr df st $ reverse $ bagToList $ srcErrorMessages src_err +sourceError :: SourceError -> ReaderM [String] +sourceError = errsToStr . reverse . bagToList . srcErrorMessages -errsToStr :: DynFlags -> PprStyle -> [ErrMsg] -> [String] -errsToStr df st = map (ppErrMsg df st) +errsToStr :: [ErrMsg] -> ReaderM [String] +errsToStr = mapM ppErrMsg ---------------------------------------------------------------- -ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String -ppErrMsg dflag st err = - ppMsg spn SevError dflag st msg ++ (if null ext then "" else "\n" ++ ext) +ppErrMsg :: ErrMsg -> ReaderM String +ppErrMsg err = do + dflag <- asks rsDynFlags + st <- asks rsPprStyle + let ext = showPage dflag st (errMsgExtraInfo err) + m <- ppMsg spn SevError msg + return $ m ++ (if null ext then "" else "\n" ++ ext) where spn = Gap.errorMsgSpan err msg = errMsgShortDoc err - ext = showPage dflag st (errMsgExtraInfo err) -ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String -ppMsg spn sev dflag st msg = prefix ++ cts - where - cts = showPage dflag st msg - prefix = ppMsgPrefix spn sev dflag st cts +ppMsg :: SrcSpan -> Severity-> SDoc -> ReaderM String +ppMsg spn sev msg = do + dflag <- asks rsDynFlags + st <- asks rsPprStyle + let cts = showPage dflag st msg + prefix <- ppMsgPrefix spn sev cts + return $ prefix ++ cts -ppMsgPrefix :: SrcSpan -> Severity-> DynFlags -> PprStyle -> String -> String -ppMsgPrefix spn sev dflag _st cts = +ppMsgPrefix :: SrcSpan -> Severity -> String -> ReaderM String +ppMsgPrefix spn sev cts = do + dflag <- asks rsDynFlags + mr <- asks rsMapFile let defaultPrefix | Gap.isDumpSplices dflag = "" | otherwise = checkErrorPrefix - in fromMaybe defaultPrefix $ do - (line,col,_,_) <- Gap.getSrcSpan spn - file <- normalise <$> Gap.getSrcFile spn - let severityCaption = Gap.showSeverityCaption sev - pref0 | or (map (\x -> x `isPrefixOf` cts) warningAsErrorPrefixes) - = file ++ ":" ++ show line ++ ":" ++ show col ++ ":" - | otherwise = file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption - return pref0 + return $ fromMaybe defaultPrefix $ do + (line,col,_,_) <- Gap.getSrcSpan spn + file <- mr <$> normalise <$> Gap.getSrcFile spn + let severityCaption = Gap.showSeverityCaption sev + pref0 | or (map (\x -> x `isPrefixOf` cts) warningAsErrorPrefixes) + = file ++ ":" ++ show line ++ ":" ++ show col ++ ":" + | otherwise = file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption + return pref0 checkErrorPrefix :: String checkErrorPrefix = "Dummy:0:0:Error:" diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 67432cc..c577338 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -382,7 +382,7 @@ chModToMod :: ChModuleName -> ModuleName chModToMod (ChModuleName mn) = mkModuleName mn -resolveModule :: (MonadIO m, GmEnv m, GmLog m, GmState m) => +resolveModule :: (IOish m, GmEnv m, GmLog m, GmState m) => HscEnv -> [FilePath] -> CompilationUnit -> m (Maybe ModulePath) resolveModule env _srcDirs (Right mn) = liftIO $ traverse canonicalizeModulePath =<< findModulePath env mn @@ -392,7 +392,7 @@ resolveModule env srcDirs (Left fn') = do Nothing -> return Nothing Just fn'' -> do fn <- liftIO $ canonicalizePath fn'' - emn <- liftIO $ fileModuleName env fn + emn <- fileModuleName env fn case emn of Left errs -> do gmLog GmWarning ("resolveModule " ++ show fn) $ From 1efacbef88550de524fb8645417d9f176836df06 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 13:17:49 +0300 Subject: [PATCH 008/147] Use less generic name for reader monad/state in Logger --- Language/Haskell/GhcMod/Logger.hs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 46ab639..bd86658 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -38,12 +38,12 @@ data Log = Log [String] Builder newtype LogRef = LogRef (IORef Log) -data ReaderState = ReaderState { rsDynFlags :: DynFlags - , rsPprStyle :: PprStyle - , rsMapFile :: FilePath -> FilePath - } +data GmPprEnv = GmPprEnv { rsDynFlags :: DynFlags + , rsPprStyle :: PprStyle + , rsMapFile :: FilePath -> FilePath + } -type ReaderM a = Reader ReaderState a +type GmPprEnvM a = Reader GmPprEnv a emptyLog :: Log emptyLog = Log [] id @@ -57,7 +57,7 @@ readAndClearLogRef (LogRef ref) = do writeIORef ref emptyLog return $ b [] -appendLogRef :: ReaderState -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () +appendLogRef :: GmPprEnv -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () appendLogRef rs df (LogRef ref) _ sev src st msg = modifyIORef ref update where l = runReader (ppMsg src sev msg) rs{rsDynFlags=df, rsPprStyle=st} @@ -101,7 +101,7 @@ withLogger' env action = do let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) stl = mkUserStyle pu AllTheWay - st = ReaderState { + st = GmPprEnv { rsDynFlags = dflags , rsPprStyle = stl , rsMapFile = \key -> @@ -131,15 +131,15 @@ errBagToStrList env errs = let ---------------------------------------------------------------- -- | Converting 'SourceError' to 'String'. -sourceError :: SourceError -> ReaderM [String] +sourceError :: SourceError -> GmPprEnvM [String] sourceError = errsToStr . reverse . bagToList . srcErrorMessages -errsToStr :: [ErrMsg] -> ReaderM [String] +errsToStr :: [ErrMsg] -> GmPprEnvM [String] errsToStr = mapM ppErrMsg ---------------------------------------------------------------- -ppErrMsg :: ErrMsg -> ReaderM String +ppErrMsg :: ErrMsg -> GmPprEnvM String ppErrMsg err = do dflag <- asks rsDynFlags st <- asks rsPprStyle @@ -150,7 +150,7 @@ ppErrMsg err = do spn = Gap.errorMsgSpan err msg = errMsgShortDoc err -ppMsg :: SrcSpan -> Severity-> SDoc -> ReaderM String +ppMsg :: SrcSpan -> Severity-> SDoc -> GmPprEnvM String ppMsg spn sev msg = do dflag <- asks rsDynFlags st <- asks rsPprStyle @@ -158,7 +158,7 @@ ppMsg spn sev msg = do prefix <- ppMsgPrefix spn sev cts return $ prefix ++ cts -ppMsgPrefix :: SrcSpan -> Severity -> String -> ReaderM String +ppMsgPrefix :: SrcSpan -> Severity -> String -> GmPprEnvM String ppMsgPrefix spn sev cts = do dflag <- asks rsDynFlags mr <- asks rsMapFile From de5ff87f19cba6c5fdad083631b4be5afbfe8211 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 13:19:57 +0300 Subject: [PATCH 009/147] Don't use infix call for type ctor --- Language/Haskell/GhcMod/Target.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index c577338..417080a 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -179,7 +179,8 @@ runGmlTWith efnmns' mdf wrapper action = do where relativize (Target (TargetFile filePath phase) taoc src) = do crdl <- cradle - let tid = makeRelative (cradleRootDir crdl) filePath `TargetFile` phase + let tid = TargetFile relativeFilePath phase + relativeFilePath = makeRelative (cradleRootDir crdl) filePath return $ Target tid taoc src relativize tgt = return tgt From 67102c92b854b4e61dba97bf08ab9cd3a33d7717 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 13:25:42 +0300 Subject: [PATCH 010/147] Use cradleTempDir instead of getTemporaryDirectory in HMG --- Language/Haskell/GhcMod/Logger.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index bd86658..c6c1598 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -126,7 +126,7 @@ errBagToStrList env errs = let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) st = mkUserStyle pu AllTheWay - in runReader (errsToStr (bagToList errs)) ReaderState{rsDynFlags=dflags, rsPprStyle=st} + in runReader (errsToStr (bagToList errs)) GmPprEnv{rsDynFlags=dflags, rsPprStyle=st} ---------------------------------------------------------------- From e15eea2f39ad3424a4a2c8d8c1fe2407c453a058 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 13:27:31 +0300 Subject: [PATCH 011/147] Rename file-map option to map-file --- src/GHCMod.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 3c512bf..3e7af78 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -271,7 +271,7 @@ globalArgSpec = reqArg "OPT" $ \g o -> Right $ o { ghcUserOptions = g : ghcUserOptions o } - , option "" ["file-map"] "Redirect one file to another, --file-map \"file1.hs=file2.hs\"" $ + , option "" ["map-file"] "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" $ reqArg "OPT" $ \g o -> let m = case second (drop 1) $ span (/='=') g of (s,"") -> (s, MemoryMapping Nothing) From 86545a895bff885f339f224e77935ca664c467e7 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 13:28:55 +0300 Subject: [PATCH 012/147] Rename load and unload interact. cmds to map-file and unmap-file --- src/GHCMod.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 3e7af78..7ccefd6 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -438,11 +438,11 @@ legacyInteractiveLoop symdbreq world = do "boot" -> bootCmd [] "browse" -> browseCmd args - "load" -> loadMappedFile arg (MemoryMapping Nothing) - >> return "" + "map-file" -> loadMappedFile arg (MemoryMapping Nothing) + >> return "" - "unload" -> unloadMappedFile arg - >> return "" + "unmap-file" -> unloadMappedFile arg + >> return "" "quit" -> liftIO $ exitSuccess "" -> liftIO $ exitSuccess From d3b1bf125b47ea50307c4ef893fcc5594e021f74 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 14:01:03 +0300 Subject: [PATCH 013/147] Move reading source from stdin to frontend --- Language/Haskell/GhcMod/FileMapping.hs | 16 ++-------------- src/GHCMod.hs | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index d40bd64..e0eb773 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -21,22 +21,10 @@ import GHC loadMappedFiles :: IOish m => GhcModT m () loadMappedFiles = do Options {fileMappings} <- options - mapM_ (uncurry loadMappedFile) $ reverse fileMappings + mapM_ (uncurry loadMappedFile) fileMappings loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () -loadMappedFile from fm@(RedirectedMapping _) = - addToState from fm -loadMappedFile from (MemoryMapping _) = do - let loop' acc = do - line <- getLine - if not (null line) && last line == '\EOT' - then return $ acc ++ init line - else loop' (acc++line++"\n") - src <- liftIO $ loop' "" - addToState from (MemoryMapping $ Just src) - -addToState :: IOish m => FilePath -> FileMapping -> GhcModT m () -addToState from fm = do +loadMappedFile from fm = do crdl <- cradle let ccfn = cradleCurrentDir crdl from cfn <- liftIO $ canonicalizePath ccfn diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 7ccefd6..1c21738 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -438,11 +438,12 @@ legacyInteractiveLoop symdbreq world = do "boot" -> bootCmd [] "browse" -> browseCmd args - "map-file" -> loadMappedFile arg (MemoryMapping Nothing) - >> return "" + "map-file" -> liftIO getFileSourceFromStdin + >>= loadMappedFile arg . MemoryMapping . Just + >> return "" - "unmap-file" -> unloadMappedFile arg - >> return "" + "unmap-file" -> unloadMappedFile arg + >> return "" "quit" -> liftIO $ exitSuccess "" -> liftIO $ exitSuccess @@ -456,6 +457,15 @@ legacyInteractiveLoop symdbreq world = do , GHandler $ \(SomeException e) -> gmErrStrLn (show e) >> return "" ] +getFileSourceFromStdin :: IO String +getFileSourceFromStdin = do + let loop' acc = do + line <- getLine + if not (null line) && last line == '\EOT' + then return $ acc ++ init line + else loop' (acc++line++"\n") + loop' "" + ghcCommands :: IOish m => [String] -> GhcModT m () ghcCommands [] = fatalError "No command given (try --help)" ghcCommands (cmd:args) = do From 5b0cca0353b95587def6139f016a3a7df2567c58 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 21:42:48 +0300 Subject: [PATCH 014/147] Added withMappedFile utility function Works pretty much as `withTempFile`, except looks mapping up and uses temp. file only when necessary. --- Language/Haskell/GhcMod/FileMappingUtils.hs | 22 +++++++++++++++++++++ Language/Haskell/GhcMod/HomeModuleGraph.hs | 3 +-- ghc-mod.cabal | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Language/Haskell/GhcMod/FileMappingUtils.hs diff --git a/Language/Haskell/GhcMod/FileMappingUtils.hs b/Language/Haskell/GhcMod/FileMappingUtils.hs new file mode 100644 index 0000000..ee26417 --- /dev/null +++ b/Language/Haskell/GhcMod/FileMappingUtils.hs @@ -0,0 +1,22 @@ +module Language.Haskell.GhcMod.FileMappingUtils where + +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types + +import System.IO +import System.FilePath +import System.Directory + +withMappedFile :: (IOish m, GmState m, GmEnv m) => forall a. FilePath -> (FilePath -> m a) -> m a +withMappedFile file action = lookupMMappedFile file >>= runWithFile + where + runWithFile (Just (RedirectedMapping to)) = action to + runWithFile (Just (MemoryMapping (Just src))) = do + crdl <- cradle + (fp,hndl) <- liftIO $ openTempFile (cradleTempDir crdl) (takeBaseName file) + liftIO $ hPutStr hndl src + liftIO $ hClose hndl + result <- action fp + liftIO $ removeFile fp + return result + runWithFile _ = action file diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index ce6fcc2..c3fa6dc 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -61,8 +61,7 @@ import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Gap (parseModuleHeader) - -import System.IO +import Language.Haskell.GhcMod.FileMappingUtils -- | Turn module graph into a graphviz dot file -- diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 8336665..5b5b547 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -106,6 +106,7 @@ Library Language.Haskell.GhcMod.DynFlags Language.Haskell.GhcMod.Error Language.Haskell.GhcMod.FileMapping + Language.Haskell.GhcMod.FileMappingUtils Language.Haskell.GhcMod.FillSig Language.Haskell.GhcMod.Find Language.Haskell.GhcMod.Flag From d405ce7efaa4731698926919fbd5464b0fcf933d Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Thu, 2 Jul 2015 21:45:07 +0300 Subject: [PATCH 015/147] Make linting work with mapped files --- Language/Haskell/GhcMod/Lint.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index c6e471a..8fc710a 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -8,6 +8,10 @@ import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Language.Haskell.HLint (hlint) +import Language.Haskell.GhcMod.FileMappingUtils + +import Data.List (stripPrefix) + -- | Checking syntax of a target file using hlint. -- Warnings and errors are returned. lint :: IOish m @@ -15,7 +19,11 @@ lint :: IOish m -> GhcModT m String lint file = do opt <- options - ghandle handler . pack =<< liftIO (hlint $ file : "--quiet" : hlintOpts opt) + withMappedFile file $ \tempfile -> + liftIO (hlint $ tempfile : "--quiet" : hlintOpts opt) + >>= mapM (replaceFileName tempfile) + >>= ghandle handler . pack where - pack = convert' . map (init . show) -- init drops the last \n. + pack = convert' . map init -- init drops the last \n. handler (SomeException e) = return $ checkErrorPrefix ++ show e ++ "\n" + replaceFileName fp s = return $ maybe (show s) (file++) $ stripPrefix fp (show s) From 31020c41125cc66be4da06cd6efd1b4a5be28eb1 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 06:43:32 +0300 Subject: [PATCH 016/147] Move withMappedFile to Language.Haskell.GhcMod.Utils --- Language/Haskell/GhcMod/FileMappingUtils.hs | 22 --------------------- Language/Haskell/GhcMod/HomeModuleGraph.hs | 2 +- Language/Haskell/GhcMod/Lint.hs | 2 +- Language/Haskell/GhcMod/Utils.hs | 21 ++++++++++++++++++-- ghc-mod.cabal | 1 - 5 files changed, 21 insertions(+), 27 deletions(-) delete mode 100644 Language/Haskell/GhcMod/FileMappingUtils.hs diff --git a/Language/Haskell/GhcMod/FileMappingUtils.hs b/Language/Haskell/GhcMod/FileMappingUtils.hs deleted file mode 100644 index ee26417..0000000 --- a/Language/Haskell/GhcMod/FileMappingUtils.hs +++ /dev/null @@ -1,22 +0,0 @@ -module Language.Haskell.GhcMod.FileMappingUtils where - -import Language.Haskell.GhcMod.Types -import Language.Haskell.GhcMod.Monad.Types - -import System.IO -import System.FilePath -import System.Directory - -withMappedFile :: (IOish m, GmState m, GmEnv m) => forall a. FilePath -> (FilePath -> m a) -> m a -withMappedFile file action = lookupMMappedFile file >>= runWithFile - where - runWithFile (Just (RedirectedMapping to)) = action to - runWithFile (Just (MemoryMapping (Just src))) = do - crdl <- cradle - (fp,hndl) <- liftIO $ openTempFile (cradleTempDir crdl) (takeBaseName file) - liftIO $ hPutStr hndl src - liftIO $ hClose hndl - result <- action fp - liftIO $ removeFile fp - return result - runWithFile _ = action file diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index c3fa6dc..a5aae65 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -61,7 +61,7 @@ import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Gap (parseModuleHeader) -import Language.Haskell.GhcMod.FileMappingUtils +import Language.Haskell.GhcMod.Utils (withMappedFile) -- | Turn module graph into a graphviz dot file -- diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index 8fc710a..735411a 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -8,7 +8,7 @@ import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Language.Haskell.HLint (hlint) -import Language.Haskell.GhcMod.FileMappingUtils +import Language.Haskell.GhcMod.Utils (withMappedFile) import Data.List (stripPrefix) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index c9da5a2..87c9a53 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -27,13 +27,15 @@ import Control.Applicative import Data.Char import Exception import Language.Haskell.GhcMod.Error +import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist, - getTemporaryDirectory, canonicalizePath) + getTemporaryDirectory, canonicalizePath, removeFile) import System.Environment import System.FilePath (splitDrive, takeDirectory, takeFileName, pathSeparators, ()) -import System.IO.Temp (createTempDirectory) +import System.IO.Temp (createTempDirectory, openTempFile) +import System.IO (hPutStr, hClose) import System.Process (readProcess) import Text.Printf @@ -157,3 +159,18 @@ canonFilePath f = do e <- doesFileExist p when (not e) $ error $ "canonFilePath: not a file: " ++ p return p + +withMappedFile :: (IOish m, GmState m, GmEnv m) => + forall a. FilePath -> (FilePath -> m a) -> m a +withMappedFile file action = lookupMMappedFile file >>= runWithFile + where + runWithFile (Just (RedirectedMapping to)) = action to + runWithFile (Just (MemoryMapping (Just src))) = do + crdl <- cradle + (fp,hndl) <- liftIO $ openTempFile (cradleTempDir crdl) (takeFileName file) + liftIO $ hPutStr hndl src + liftIO $ hClose hndl + result <- action fp + liftIO $ removeFile fp + return result + runWithFile _ = action file diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 5b5b547..8336665 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -106,7 +106,6 @@ Library Language.Haskell.GhcMod.DynFlags Language.Haskell.GhcMod.Error Language.Haskell.GhcMod.FileMapping - Language.Haskell.GhcMod.FileMappingUtils Language.Haskell.GhcMod.FillSig Language.Haskell.GhcMod.Find Language.Haskell.GhcMod.Flag From a0cf585939e2285e3c4c46ea17bb128db8e8ad01 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 19:12:04 +0300 Subject: [PATCH 017/147] Bugfix: canonicalize filename before trying to unload --- Language/Haskell/GhcMod/FileMapping.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index e0eb773..8904efa 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -24,11 +24,8 @@ loadMappedFiles = do mapM_ (uncurry loadMappedFile) fileMappings loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () -loadMappedFile from fm = do - crdl <- cradle - let ccfn = cradleCurrentDir crdl from - cfn <- liftIO $ canonicalizePath ccfn - addMMappedFile cfn fm +loadMappedFile from fm = + getCanonicalFileName from >>= (`addMMappedFile` fm) mapFile :: (IOish m, GmState m, GhcMonad m) => HscEnv -> Target -> m Target @@ -51,5 +48,11 @@ mkMappedTarget tid taoc (Just (MemoryMapping (Just src))) = do return $ mkTarget tid taoc $ Just (sb, ct) mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing +getCanonicalFileName :: IOish m => FilePath -> GhcModT m FilePath +getCanonicalFileName fn = do + crdl <- cradle + let ccfn = cradleCurrentDir crdl fn + liftIO $ canonicalizePath ccfn + unloadMappedFile :: IOish m => FilePath -> GhcModT m () -unloadMappedFile = delMMappedFile +unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileName From 267edfebe3a3e14d363213fe3ace290f34de4705 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 19:35:22 +0300 Subject: [PATCH 018/147] Try best-guess tactic if file to be redirected doesn't exist --- Language/Haskell/GhcMod/FileMapping.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index 8904efa..93f7cb1 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -52,7 +52,10 @@ getCanonicalFileName :: IOish m => FilePath -> GhcModT m FilePath getCanonicalFileName fn = do crdl <- cradle let ccfn = cradleCurrentDir crdl fn - liftIO $ canonicalizePath ccfn + fex <- liftIO $ doesFileExist ccfn + if fex + then liftIO $ canonicalizePath ccfn + else return ccfn unloadMappedFile :: IOish m => FilePath -> GhcModT m () unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileName From 438b15463e7dd41d78e3d1c5ca4bf10a4610e8f8 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 20:25:54 +0300 Subject: [PATCH 019/147] Move getCanonicalFileName(Safe) to Utils module --- Language/Haskell/GhcMod/FileMapping.hs | 17 +++-------------- Language/Haskell/GhcMod/Utils.hs | 9 +++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index 93f7cb1..bae425e 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -9,9 +9,7 @@ import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Gap import Language.Haskell.GhcMod.HomeModuleGraph - -import System.Directory -import System.FilePath +import Language.Haskell.GhcMod.Utils import Data.Time @@ -25,7 +23,7 @@ loadMappedFiles = do loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () loadMappedFile from fm = - getCanonicalFileName from >>= (`addMMappedFile` fm) + getCanonicalFileNameSafe from >>= (`addMMappedFile` fm) mapFile :: (IOish m, GmState m, GhcMonad m) => HscEnv -> Target -> m Target @@ -48,14 +46,5 @@ mkMappedTarget tid taoc (Just (MemoryMapping (Just src))) = do return $ mkTarget tid taoc $ Just (sb, ct) mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing -getCanonicalFileName :: IOish m => FilePath -> GhcModT m FilePath -getCanonicalFileName fn = do - crdl <- cradle - let ccfn = cradleCurrentDir crdl fn - fex <- liftIO $ doesFileExist ccfn - if fex - then liftIO $ canonicalizePath ccfn - else return ccfn - unloadMappedFile :: IOish m => FilePath -> GhcModT m () -unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileName +unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileNameSafe diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 87c9a53..209b983 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -174,3 +174,12 @@ withMappedFile file action = lookupMMappedFile file >>= runWithFile liftIO $ removeFile fp return result runWithFile _ = action file + +getCanonicalFileNameSafe :: IOish m => FilePath -> GhcModT m FilePath +getCanonicalFileNameSafe fn = do + crdl <- cradle + let ccfn = cradleCurrentDir crdl fn + fex <- liftIO $ doesFileExist ccfn + if fex + then liftIO $ canonicalizePath ccfn + else return ccfn From c5f71933f60beab372dc8f21635d9b00ab4e21ad Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 20:29:07 +0300 Subject: [PATCH 020/147] Canonicalize FilePath before lookup in withMappedFile --- Language/Haskell/GhcMod/Utils.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 209b983..73d2fed 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -162,7 +162,7 @@ canonFilePath f = do withMappedFile :: (IOish m, GmState m, GmEnv m) => forall a. FilePath -> (FilePath -> m a) -> m a -withMappedFile file action = lookupMMappedFile file >>= runWithFile +withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile >>= runWithFile where runWithFile (Just (RedirectedMapping to)) = action to runWithFile (Just (MemoryMapping (Just src))) = do @@ -175,7 +175,7 @@ withMappedFile file action = lookupMMappedFile file >>= runWithFile return result runWithFile _ = action file -getCanonicalFileNameSafe :: IOish m => FilePath -> GhcModT m FilePath +getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath getCanonicalFileNameSafe fn = do crdl <- cradle let ccfn = cradleCurrentDir crdl fn From eb280357534460a401a958ab012fde98cae60209 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 21:35:57 +0300 Subject: [PATCH 021/147] Some file mapping tests --- ghc-mod.cabal | 2 + test/FileMappingSpec.hs | 134 ++++++++++++++++++++++ test/data/file-mapping/File.hs | 2 + test/data/file-mapping/File_Redir.hs | 1 + test/data/file-mapping/File_Redir_Lint.hs | 4 + 5 files changed, 143 insertions(+) create mode 100644 test/FileMappingSpec.hs create mode 100644 test/data/file-mapping/File.hs create mode 100644 test/data/file-mapping/File_Redir.hs create mode 100644 test/data/file-mapping/File_Redir_Lint.hs diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 8336665..357f55e 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -81,6 +81,7 @@ Extra-Source-Files: ChangeLog test/data/cabal-preprocessors/*.cabal test/data/cabal-preprocessors/*.hs test/data/cabal-preprocessors/*.hsc + test/data/file-mapping/*.hs Library Default-Language: Haskell2010 @@ -237,6 +238,7 @@ Test-Suite spec MonadSpec PathsAndFilesSpec HomeModuleGraphSpec + FileMappingSpec Build-Depends: hspec >= 2.0.0 if impl(ghc == 7.4.*) diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs new file mode 100644 index 0000000..91576a3 --- /dev/null +++ b/test/FileMappingSpec.hs @@ -0,0 +1,134 @@ +module FileMappingSpec where + +import Language.Haskell.GhcMod.FileMapping +import Language.Haskell.GhcMod.Utils (withMappedFile) +import Test.Hspec +import TestUtils +import qualified Data.Map as M +import Dir +import Data.Maybe + +import Language.Haskell.GhcMod + +spec :: Spec +spec = do + describe "loadMappedFile" $ do + it "inserts a given FilePath FileMapping into state with canonicalized path" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFile "File.hs" (MemoryMapping Nothing) + getMMappedFiles + dir <- getCurrentDirectory + show mappedFiles `shouldBe` show (M.fromList [(dir "File.hs", MemoryMapping Nothing)]) + it "should try to guess a canonical name if file doesn't exist" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFile "NonExistantFile.hs" (MemoryMapping Nothing) + getMMappedFiles + dir <- getCurrentDirectory + show mappedFiles `shouldBe` show (M.fromList [(dir "NonExistantFile.hs", MemoryMapping Nothing)]) + + describe "unloadMappedFile" $ do + it "removes a given FilePath from state" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFile "File.hs" (MemoryMapping Nothing) + unloadMappedFile "File.hs" + getMMappedFiles + show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)])) + it "should work even if file does not exist" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFile "NonExistantFile.hs" (MemoryMapping Nothing) + unloadMappedFile "NonExistantFile.hs" + getMMappedFiles + show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)])) + + describe "loadMappedFiles" $ do + it "loads all file mappings passed as Options" $ do + let fm = [("File.hs", RedirectedMapping "File_Redir.hs"), ("File2.hs", MemoryMapping Nothing)] + mappedFiles <- run defaultOptions { fileMappings = fm } $ + loadMappedFiles >> getMMappedFiles + dir <- getCurrentDirectory + M.lookup (dir "File.hs") mappedFiles `shouldSatisfy` isJust + M.lookup (dir "File2.hs") mappedFiles `shouldSatisfy` isJust + it "prioritizes latter occurence of the same file" $ do + let fm = [("File.hs", RedirectedMapping "File_Redir.hs"), ("File.hs", MemoryMapping Nothing)] + mappedFiles <- run defaultOptions { fileMappings = fm } $ + loadMappedFiles >> getMMappedFiles + dir <- getCurrentDirectory + show (M.lookup (dir "File.hs") mappedFiles) `shouldBe` show (Just (MemoryMapping Nothing)) + + describe "withMappedFile" $ do + it "checks if there is a redirected file and calls and action with its FilePath" $ do + withDirectory_ "test/data/file-mapping" $ do + res <- runD $ do + loadMappedFile "File.hs" (RedirectedMapping "File_Redir.hs") + withMappedFile "File.hs" return + res `shouldBe` "File_Redir.hs" + it "checks if there is an in-memory file and calls and action with temporary file" $ do + withDirectory_ "test/data/file-mapping" $ do + (fn, src) <- runD $ do + loadMappedFile "File.hs" (MemoryMapping $ Just "main = test") + withMappedFile "File.hs" $ \fn -> do + src <- liftIO $ readFile fn + return (fn, src) + fn `shouldSatisfy` (/="File.hs") + src `shouldBe` "main = test" + it "runs action with original filename if there is no mapping" $ do + withDirectory_ "test/data/file-mapping" $ do + fn <- runD $ do + withMappedFile "File.hs" return + fn `shouldBe` "File.hs" + + describe "integration tests" $ do + it "checks redirected file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping" $ do + let fm = [("File.hs", RedirectedMapping "File_Redir.hs")] + res <- run defaultOptions {fileMappings = fm} $ do + loadMappedFiles + checkSyntax ["File.hs"] + res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: forall t. t\n" + it "checks in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping" $ do + let fm = [("File.hs", MemoryMapping $ Just "main = putStrLn \"Hello World!\"\n")] + res <- run defaultOptions {fileMappings = fm} $ do + loadMappedFiles + checkSyntax ["File.hs"] + res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "lints redirected file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping" $ do + res <- runD $ do + loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + lint "File.hs" + res `shouldBe` "File.hs:4:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" + it "lints in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping" $ do + res <- runD $ do + loadMappedFile "File.hs" (MemoryMapping $ Just "func a b = (++) a b\n") + lint "File.hs" + res `shouldBe` "File.hs:1:1: Error: Eta reduce\NULFound:\NUL func a b = (++) a b\NULWhy not:\NUL func = (++)\n" + it "shows types of the expression for redirected files" $ do + let tdir = "test/data/file-mapping" + res <- runD' tdir $ do + loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + types "File.hs" 4 12 + res `shouldBe` "4 12 4 15 \"a -> a -> a\"\n4 12 4 17 \"a -> a\"\n4 12 4 19 \"a\"\n4 1 4 19 \"a -> a -> a\"\n" + it "shows types of the expression for in-memory files" $ do + let tdir = "test/data/file-mapping" + res <- runD' tdir $ do + loadMappedFile "File.hs" (MemoryMapping $ Just "main = putStrLn \"Hello!\"") + types "File.hs" 1 14 + res `shouldBe` "1 8 1 16 \"String -> IO ()\"\n1 8 1 25 \"IO ()\"\n1 1 1 25 \"IO ()\"\n" + it "shows info for the expression for redirected files" $ do + let tdir = "test/data/file-mapping" + res <- runD' tdir $ do + loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + info "File.hs" $ Expression "func" + res `shouldBe` "func :: Num a => a -> a -> a \t-- Defined at File.hs:4:1\n" + it "shows info for the expression for in-memory files" $ do + let tdir = "test/data/file-mapping" + res <- runD' tdir $ do + loadMappedFile "File.hs" (MemoryMapping $ Just "module File where\n\ntestfun = putStrLn \"Hello!\"") + info "File.hs" $ Expression "testfun" + res `shouldBe` "testfun :: IO () \t-- Defined at File.hs:3:1\n" diff --git a/test/data/file-mapping/File.hs b/test/data/file-mapping/File.hs new file mode 100644 index 0000000..1f61122 --- /dev/null +++ b/test/data/file-mapping/File.hs @@ -0,0 +1,2 @@ +main :: IO () +main = putStrLn "Hello World!" diff --git a/test/data/file-mapping/File_Redir.hs b/test/data/file-mapping/File_Redir.hs new file mode 100644 index 0000000..45846a9 --- /dev/null +++ b/test/data/file-mapping/File_Redir.hs @@ -0,0 +1 @@ +main = undefined diff --git a/test/data/file-mapping/File_Redir_Lint.hs b/test/data/file-mapping/File_Redir_Lint.hs new file mode 100644 index 0000000..da61368 --- /dev/null +++ b/test/data/file-mapping/File_Redir_Lint.hs @@ -0,0 +1,4 @@ +module File_Redir_Lint where + +func :: Num a => a -> a -> a +func a b = (*) a b From 0ad438461b40fc574edcba2b570f0fd56bdf1ea9 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 22:30:17 +0300 Subject: [PATCH 022/147] Fix mistake in test file Should be `module File`, since it's used as a redirection. --- test/data/file-mapping/File_Redir_Lint.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/file-mapping/File_Redir_Lint.hs b/test/data/file-mapping/File_Redir_Lint.hs index da61368..461ebdd 100644 --- a/test/data/file-mapping/File_Redir_Lint.hs +++ b/test/data/file-mapping/File_Redir_Lint.hs @@ -1,4 +1,4 @@ -module File_Redir_Lint where +module File where func :: Num a => a -> a -> a func a b = (*) a b From 8f931eb928cfe267a73407978be2358ad801ad1c Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Fri, 3 Jul 2015 22:31:52 +0300 Subject: [PATCH 023/147] Fix problem with fileModSummary --- Language/Haskell/GhcMod/CaseSplit.hs | 3 ++- Language/Haskell/GhcMod/FileMapping.hs | 9 +++++++++ Language/Haskell/GhcMod/FillSig.hs | 7 ++++--- Language/Haskell/GhcMod/Info.hs | 3 ++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 77603ca..5d2c414 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -26,6 +26,7 @@ import Language.Haskell.GhcMod.SrcUtils import Language.Haskell.GhcMod.Doc import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.FileMapping (fileModSummaryWithMapping) ---------------------------------------------------------------- -- CASE SPLITTING @@ -51,7 +52,7 @@ splits file lineNo colNo = crdl <- cradle style <- getStyle dflag <- G.getSessionDynFlags - modSum <- Gap.fileModSummary (cradleCurrentDir crdl file) + modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) whenFound' opt (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of (SplitInfo varName bndLoc (varLoc,varT) _matches) -> do let varName' = showName dflag style varName -- Convert name to string diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index bae425e..5d95868 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -3,6 +3,7 @@ module Language.Haskell.GhcMod.FileMapping , loadMappedFiles , unloadMappedFile , mapFile + , fileModSummaryWithMapping ) where import Language.Haskell.GhcMod.Types @@ -48,3 +49,11 @@ mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing unloadMappedFile :: IOish m => FilePath -> GhcModT m () unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileNameSafe + +fileModSummaryWithMapping :: (IOish m, GmState m, GhcMonad m, GmEnv m) => + FilePath -> m ModSummary +fileModSummaryWithMapping fn = do + mmf <- getCanonicalFileNameSafe fn >>= lookupMMappedFile + case mmf of + Just (RedirectedMapping to) -> fileModSummary to + _ -> fileModSummary fn diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index 1f65f93..02da7a0 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -27,6 +27,7 @@ import Language.Haskell.GhcMod.Logging (gmLog) import Language.Haskell.GhcMod.Pretty (showDoc) import Language.Haskell.GhcMod.Doc import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.FileMapping (fileModSummaryWithMapping) import Outputable (PprStyle) import qualified Type as Ty import qualified HsBinds as Ty @@ -76,7 +77,7 @@ sig file lineNo colNo = opt <- options style <- getStyle dflag <- G.getSessionDynFlags - modSum <- Gap.fileModSummary file + modSum <- fileModSummaryWithMapping file whenFound opt (getSignature modSum lineNo colNo) $ \s -> case s of Signature loc names ty -> @@ -345,7 +346,7 @@ refine file lineNo colNo (Expression expr) = opt <- options style <- getStyle dflag <- G.getSessionDynFlags - modSum <- Gap.fileModSummary file + modSum <- fileModSummaryWithMapping file p <- G.parseModule modSum tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p ety <- G.exprType expr @@ -422,7 +423,7 @@ auto file lineNo colNo = opt <- options style <- getStyle dflag <- G.getSessionDynFlags - modSum <- Gap.fileModSummary file + modSum <- fileModSummaryWithMapping file p <- G.parseModule modSum tcm@TypecheckedModule { tm_typechecked_source = tcs diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index be32635..23e5950 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -22,6 +22,7 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.FileMapping (fileModSummaryWithMapping) ---------------------------------------------------------------- @@ -60,7 +61,7 @@ types file lineNo colNo = runGmlT' [Left file] deferErrors $ withContext $ do crdl <- cradle - modSum <- Gap.fileModSummary (cradleCurrentDir crdl file) + modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) srcSpanTypes <- getSrcSpanType modSum lineNo colNo dflag <- G.getSessionDynFlags st <- getStyle From c2ff5be4ea729e9ae4ae069224bd6634f3b6d4f0 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sat, 4 Jul 2015 01:10:17 +0300 Subject: [PATCH 024/147] Better test for redirected check Output now doesn't depend on compiler version --- test/FileMappingSpec.hs | 2 +- test/data/file-mapping/File_Redir.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index 91576a3..ccd39be 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -88,7 +88,7 @@ spec = do res <- run defaultOptions {fileMappings = fm} $ do loadMappedFiles checkSyntax ["File.hs"] - res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: forall t. t\n" + res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "checks in-memory file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do let fm = [("File.hs", MemoryMapping $ Just "main = putStrLn \"Hello World!\"\n")] diff --git a/test/data/file-mapping/File_Redir.hs b/test/data/file-mapping/File_Redir.hs index 45846a9..d5e55cc 100644 --- a/test/data/file-mapping/File_Redir.hs +++ b/test/data/file-mapping/File_Redir.hs @@ -1 +1 @@ -main = undefined +main = putStrLn "Hello World!" From e7329a9d246c80504b0506f24efd7f66de656dde Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sat, 4 Jul 2015 17:49:48 +0300 Subject: [PATCH 025/147] Replace redirected filenames in info. --- Language/Haskell/GhcMod/Gap.hs | 42 +++++++++++++++++++++---------- Language/Haskell/GhcMod/Info.hs | 6 +++-- Language/Haskell/GhcMod/Logger.hs | 23 ++++------------- Language/Haskell/GhcMod/Utils.hs | 18 ++++++++++++- 4 files changed, 55 insertions(+), 34 deletions(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index c8b6e0f..007976c 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -67,6 +67,7 @@ import TcType import Var (varType) import System.Directory +import qualified Name import qualified InstEnv import qualified Pretty import qualified StringBuffer as SB @@ -328,8 +329,8 @@ filterOutChildren get_thing xs where implicits = mkNameSet [getName t | x <- xs, t <- implicitTyThings (get_thing x)] -infoThing :: GhcMonad m => Expression -> m SDoc -infoThing (Expression str) = do +infoThing :: GhcMonad m => (FilePath -> FilePath) -> Expression -> m SDoc +infoThing m (Expression str) = do names <- parseName str #if __GLASGOW_HASKELL__ >= 708 mb_stuffs <- mapM (getInfo False) names @@ -338,30 +339,45 @@ infoThing (Expression str) = do mb_stuffs <- mapM getInfo names let filtered = filterOutChildren (\(t,_f,_i) -> t) (catMaybes mb_stuffs) #endif - return $ vcat (intersperse (text "") $ map (pprInfo False) filtered) + return $ vcat (intersperse (text "") $ map (pprInfo m False) filtered) #if __GLASGOW_HASKELL__ >= 708 -pprInfo :: Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst]) -> SDoc -pprInfo _ (thing, fixity, insts, famInsts) - = pprTyThingInContextLoc thing +pprInfo :: (FilePath -> FilePath) -> Bool -> (TyThing, GHC.Fixity, [ClsInst], [FamInst]) -> SDoc +pprInfo m _ (thing, fixity, insts, famInsts) + = pprTyThingInContextLoc' thing $$ show_fixity fixity $$ InstEnv.pprInstances insts $$ pprFamInsts famInsts - where - show_fixity fx - | fx == defaultFixity = Outputable.empty - | otherwise = ppr fx <+> ppr (getName thing) #else -pprInfo :: PrintExplicitForalls -> (TyThing, GHC.Fixity, [ClsInst]) -> SDoc -pprInfo pefas (thing, fixity, insts) - = pprTyThingInContextLoc pefas thing +pprInfo :: (FilePath -> FilePath) -> PrintExplicitForalls -> (TyThing, GHC.Fixity, [ClsInst]) -> SDoc +pprInfo m pefas (thing, fixity, insts) + = pprTyThingInContextLoc' pefas thing $$ show_fixity fixity $$ vcat (map pprInstance insts) +#endif where show_fixity fx | fx == defaultFixity = Outputable.empty | otherwise = ppr fx <+> ppr (getName thing) +#if __GLASGOW_HASKELL__ >= 708 + pprTyThingInContextLoc' thing' = hang (pprTyThingInContext thing') 2 + (char '\t' <> ptext (sLit "--") <+> loc) + where loc = ptext (sLit "Defined") <+> pprNameDefnLoc' (getName thing') +#else + pprTyThingInContextLoc' pefas thing' = hang (pprTyThingInContext pefas thing') 2 + (char '\t' <> ptext (sLit "--") <+> loc) + where loc = ptext (sLit "Defined") <+> pprNameDefnLoc' (getName thing') #endif + pprNameDefnLoc' name + = case Name.nameSrcLoc name of + RealSrcLoc s -> ptext (sLit "at") <+> ppr (subst s) + UnhelpfulLoc s + | Name.isInternalName name || Name.isSystemName name + -> ptext (sLit "at") <+> ftext s + | otherwise + -> ptext (sLit "in") <+> quotes (ppr (nameModule name)) + where subst s = mkRealSrcLoc (realFP s) (srcLocLine s) (srcLocCol s) + realFP = mkFastString . m . unpackFS . srcLocFile ---------------------------------------------------------------- ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index 23e5950..f22451d 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -22,6 +22,7 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.SrcUtils import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Utils (mkRevRedirMapFunc) import Language.Haskell.GhcMod.FileMapping (fileModSummaryWithMapping) ---------------------------------------------------------------- @@ -41,9 +42,10 @@ info file expr = gmLog GmException "info" $ text "" $$ nest 4 (showDoc ex) convert' "Cannot show info" - body :: GhcMonad m => m String + body :: (GhcMonad m, GmState m, GmEnv m) => m String body = do - sdoc <- Gap.infoThing expr + m <- mkRevRedirMapFunc + sdoc <- Gap.infoThing m expr st <- getStyle dflag <- G.getSessionDynFlags return $ showPage dflag st sdoc diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index c6c1598..1d3f38a 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -9,11 +9,10 @@ module Language.Haskell.GhcMod.Logger ( import Control.Arrow import Control.Applicative import Data.List (isPrefixOf) -import qualified Data.Map as Map -import Data.Maybe (fromMaybe, mapMaybe) +import Data.Maybe (fromMaybe) import Control.Monad.Reader (Reader, asks, runReader) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) -import System.FilePath (normalise, makeRelative) +import System.FilePath (normalise) import Text.PrettyPrint import ErrUtils (ErrMsg, errMsgShortDoc, errMsgExtraInfo) @@ -28,7 +27,7 @@ import Language.Haskell.GhcMod.Doc (showPage) import Language.Haskell.GhcMod.DynFlags (withDynFlags) import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Error -import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Utils (mkRevRedirMapFunc) import qualified Language.Haskell.GhcMod.Gap as Gap import Prelude @@ -87,16 +86,7 @@ withLogger' :: (IOish m, GmState m, GmEnv m) withLogger' env action = do logref <- liftIO $ newLogRef - rfm <- do - mm <- Map.toList <$> getMMappedFiles - let - mf :: FilePath -> FileMapping -> Maybe (FilePath, FilePath) - mf from (RedirectedMapping to) - = Just (to, from) - mf _ _ = Nothing - return $ Map.fromList $ mapMaybe (uncurry mf) mm - - crdl <- cradle + rfm <- mkRevRedirMapFunc let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) @@ -104,10 +94,7 @@ withLogger' env action = do st = GmPprEnv { rsDynFlags = dflags , rsPprStyle = stl - , rsMapFile = \key -> - fromMaybe key - $ makeRelative (cradleRootDir crdl) - <$> Map.lookup key rfm + , rsMapFile = rfm } setLogger df = Gap.setLogAction df $ appendLogRef st df logref diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 73d2fed..c4f2710 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -25,6 +25,8 @@ module Language.Haskell.GhcMod.Utils ( import Control.Applicative import Data.Char +import qualified Data.Map as M +import Data.Maybe (mapMaybe, fromMaybe) import Exception import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Types @@ -33,7 +35,7 @@ import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist getTemporaryDirectory, canonicalizePath, removeFile) import System.Environment import System.FilePath (splitDrive, takeDirectory, takeFileName, pathSeparators, - ()) + (), makeRelative) import System.IO.Temp (createTempDirectory, openTempFile) import System.IO (hPutStr, hClose) import System.Process (readProcess) @@ -183,3 +185,17 @@ getCanonicalFileNameSafe fn = do if fex then liftIO $ canonicalizePath ccfn else return ccfn + +mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath) +mkRevRedirMapFunc = do + rm <- M.fromList <$> mapMaybe (uncurry mf) <$> M.toList <$> getMMappedFiles + crdl <- cradle + return $ \key -> + fromMaybe key + $ makeRelative (cradleRootDir crdl) + <$> M.lookup key rm + where + mf :: FilePath -> FileMapping -> Maybe (FilePath, FilePath) + mf from (RedirectedMapping to) + = Just (to, from) + mf _ _ = Nothing From 72c43a921096d557b089ed9bffb5dab2122deb47 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 11 Aug 2015 18:57:17 +0300 Subject: [PATCH 026/147] =?UTF-8?q?Rename=20GmPprEnv=20fields=20rs*=20?= =?UTF-8?q?=E2=86=92=20gpe*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Language/Haskell/GhcMod/Logger.hs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 1d3f38a..8389c18 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -37,9 +37,9 @@ data Log = Log [String] Builder newtype LogRef = LogRef (IORef Log) -data GmPprEnv = GmPprEnv { rsDynFlags :: DynFlags - , rsPprStyle :: PprStyle - , rsMapFile :: FilePath -> FilePath +data GmPprEnv = GmPprEnv { gpeDynFlags :: DynFlags + , gpePprStyle :: PprStyle + , gpeMapFile :: FilePath -> FilePath } type GmPprEnvM a = Reader GmPprEnv a @@ -59,7 +59,7 @@ readAndClearLogRef (LogRef ref) = do appendLogRef :: GmPprEnv -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () appendLogRef rs df (LogRef ref) _ sev src st msg = modifyIORef ref update where - l = runReader (ppMsg src sev msg) rs{rsDynFlags=df, rsPprStyle=st} + l = runReader (ppMsg src sev msg) rs{gpeDynFlags=df, gpePprStyle=st} update lg@(Log ls b) | l `elem` ls = lg | otherwise = Log (l:ls) (b . (l:)) @@ -92,9 +92,9 @@ withLogger' env action = do pu = icPrintUnqual dflags (hsc_IC env) stl = mkUserStyle pu AllTheWay st = GmPprEnv { - rsDynFlags = dflags - , rsPprStyle = stl - , rsMapFile = rfm + gpeDynFlags = dflags + , gpePprStyle = stl + , gpeMapFile = rfm } setLogger df = Gap.setLogAction df $ appendLogRef st df logref @@ -128,8 +128,8 @@ errsToStr = mapM ppErrMsg ppErrMsg :: ErrMsg -> GmPprEnvM String ppErrMsg err = do - dflag <- asks rsDynFlags - st <- asks rsPprStyle + dflag <- asks gpeDynFlags + st <- asks gpePprStyle let ext = showPage dflag st (errMsgExtraInfo err) m <- ppMsg spn SevError msg return $ m ++ (if null ext then "" else "\n" ++ ext) @@ -139,16 +139,16 @@ ppErrMsg err = do ppMsg :: SrcSpan -> Severity-> SDoc -> GmPprEnvM String ppMsg spn sev msg = do - dflag <- asks rsDynFlags - st <- asks rsPprStyle + dflag <- asks gpeDynFlags + st <- asks gpePprStyle let cts = showPage dflag st msg prefix <- ppMsgPrefix spn sev cts return $ prefix ++ cts ppMsgPrefix :: SrcSpan -> Severity -> String -> GmPprEnvM String ppMsgPrefix spn sev cts = do - dflag <- asks rsDynFlags - mr <- asks rsMapFile + dflag <- asks gpeDynFlags + mr <- asks gpeMapFile let defaultPrefix | Gap.isDumpSplices dflag = "" | otherwise = checkErrorPrefix From a9b98e7128a6efedcb399d57fdd6edb9a7683a6c Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Tue, 11 Aug 2015 19:39:23 +0300 Subject: [PATCH 027/147] [File-map] Export more user-friendly functions in public API --- Language/Haskell/GhcMod.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index d17d0e8..b9e7050 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -65,8 +65,8 @@ module Language.Haskell.GhcMod ( , gmUnsafePutStrLn , gmUnsafeErrStrLn -- * FileMapping - , getMMappedFiles - , setMMappedFiles + , loadMappedFile + , unloadMappedFile ) where import Language.Haskell.GhcMod.Boot @@ -88,3 +88,4 @@ import Language.Haskell.GhcMod.PkgDoc import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.Output +import Language.Haskell.GhcMod.FileMapping From c96abfc422f23dda0a26fc5be44a352e61db0eac Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Wed, 12 Aug 2015 18:01:01 +0300 Subject: [PATCH 028/147] Drop loadMappedFiles and move all loading code to progMain --- Language/Haskell/GhcMod/FileMapping.hs | 6 ------ Language/Haskell/GhcMod/Logger.hs | 2 +- src/GHCMod.hs | 14 ++++++++++---- test/FileMappingSpec.hs | 24 ++++-------------------- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index 5d95868..baca49b 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -1,6 +1,5 @@ module Language.Haskell.GhcMod.FileMapping ( loadMappedFile - , loadMappedFiles , unloadMappedFile , mapFile , fileModSummaryWithMapping @@ -17,11 +16,6 @@ import Data.Time import Control.Monad.Trans.Maybe import GHC -loadMappedFiles :: IOish m => GhcModT m () -loadMappedFiles = do - Options {fileMappings} <- options - mapM_ (uncurry loadMappedFile) fileMappings - loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () loadMappedFile from fm = getCanonicalFileNameSafe from >>= (`addMMappedFile` fm) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 8389c18..57fc650 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -113,7 +113,7 @@ errBagToStrList env errs = let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) st = mkUserStyle pu AllTheWay - in runReader (errsToStr (bagToList errs)) GmPprEnv{rsDynFlags=dflags, rsPprStyle=st} + in runReader (errsToStr (bagToList errs)) GmPprEnv{gpeDynFlags=dflags, gpePprStyle=st} ---------------------------------------------------------------- diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 1c21738..c7f5030 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -359,9 +359,17 @@ main = do progMain :: (Options,[String]) -> IO () progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do + let + loadMMappedFiles from (MemoryMapping Nothing) = do + src <- liftIO getFileSourceFromStdin + return (from, MemoryMapping $ Just src) + loadMMappedFiles from x = return (from, x) + fileMappings' <- forM (reverse $ fileMappings globalOptions) $ uncurry loadMMappedFiles case globalCommands cmdArgs of Just s -> gmPutStr s - Nothing -> ghcCommands cmdArgs + Nothing -> do + mapM_ (uncurry loadMappedFile) fileMappings' + ghcCommands cmdArgs where hndle action = do (e, _l) <- action @@ -468,9 +476,7 @@ getFileSourceFromStdin = do ghcCommands :: IOish m => [String] -> GhcModT m () ghcCommands [] = fatalError "No command given (try --help)" -ghcCommands (cmd:args) = do - loadMappedFiles - gmPutStr =<< action args +ghcCommands (cmd:args) = gmPutStr =<< action args where action = case cmd of _ | cmd == "list" || cmd == "modules" -> modulesCmd diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index ccd39be..18c583a 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -6,7 +6,6 @@ import Test.Hspec import TestUtils import qualified Data.Map as M import Dir -import Data.Maybe import Language.Haskell.GhcMod @@ -44,21 +43,6 @@ spec = do getMMappedFiles show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)])) - describe "loadMappedFiles" $ do - it "loads all file mappings passed as Options" $ do - let fm = [("File.hs", RedirectedMapping "File_Redir.hs"), ("File2.hs", MemoryMapping Nothing)] - mappedFiles <- run defaultOptions { fileMappings = fm } $ - loadMappedFiles >> getMMappedFiles - dir <- getCurrentDirectory - M.lookup (dir "File.hs") mappedFiles `shouldSatisfy` isJust - M.lookup (dir "File2.hs") mappedFiles `shouldSatisfy` isJust - it "prioritizes latter occurence of the same file" $ do - let fm = [("File.hs", RedirectedMapping "File_Redir.hs"), ("File.hs", MemoryMapping Nothing)] - mappedFiles <- run defaultOptions { fileMappings = fm } $ - loadMappedFiles >> getMMappedFiles - dir <- getCurrentDirectory - show (M.lookup (dir "File.hs") mappedFiles) `shouldBe` show (Just (MemoryMapping Nothing)) - describe "withMappedFile" $ do it "checks if there is a redirected file and calls and action with its FilePath" $ do withDirectory_ "test/data/file-mapping" $ do @@ -85,15 +69,15 @@ spec = do it "checks redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do let fm = [("File.hs", RedirectedMapping "File_Redir.hs")] - res <- run defaultOptions {fileMappings = fm} $ do - loadMappedFiles + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "checks in-memory file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do let fm = [("File.hs", MemoryMapping $ Just "main = putStrLn \"Hello World!\"\n")] - res <- run defaultOptions {fileMappings = fm} $ do - loadMappedFiles + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "lints redirected file if one is specified and outputs original filename" $ do From d276b9bb7f0b45e71488efb0e079f8f0090c0897 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sat, 15 Aug 2015 21:26:33 +0300 Subject: [PATCH 029/147] Remove redundant imports --- Language/Haskell/GhcMod/HomeModuleGraph.hs | 1 - Language/Haskell/GhcMod/Monad/Types.hs | 1 - Language/Haskell/GhcMod/Target.hs | 1 - 3 files changed, 3 deletions(-) diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index a5aae65..f9e6577 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -61,7 +61,6 @@ import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Gap (parseModuleHeader) -import Language.Haskell.GhcMod.Utils (withMappedFile) -- | Turn module graph into a graphviz dot file -- diff --git a/Language/Haskell/GhcMod/Monad/Types.hs b/Language/Haskell/GhcMod/Monad/Types.hs index 1339513..de5c3d0 100644 --- a/Language/Haskell/GhcMod/Monad/Types.hs +++ b/Language/Haskell/GhcMod/Monad/Types.hs @@ -105,7 +105,6 @@ import qualified Control.Monad.IO.Class as MTL import Data.Monoid (Monoid) #endif -import Data.Map (Map, empty) import qualified Data.Map as M import Data.Maybe import Data.Monoid diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 417080a..5cd5b94 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -53,7 +53,6 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) import qualified Data.Set as Set -import Data.List (nubBy) import Data.Function (on) import Distribution.Helper import Prelude hiding ((.)) From 70d2a4704bf42f3cb66ae90168a255fa2dc560e2 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 17:36:10 +0300 Subject: [PATCH 030/147] Tests for TH, LHS and CPP with FileMapping --- test/FileMappingSpec.hs | 85 +++++++++++++++++++ test/data/file-mapping/lhs/File.lhs | 2 + test/data/file-mapping/lhs/File_Redir.lhs | 1 + .../data/file-mapping/lhs/File_Redir_Lint.lhs | 4 + test/data/file-mapping/preprocessor/File.hs | 7 ++ .../file-mapping/preprocessor/File_Redir.hs | 6 ++ .../preprocessor/File_Redir_Lint.hs | 9 ++ 7 files changed, 114 insertions(+) create mode 100644 test/data/file-mapping/lhs/File.lhs create mode 100644 test/data/file-mapping/lhs/File_Redir.lhs create mode 100644 test/data/file-mapping/lhs/File_Redir_Lint.lhs create mode 100644 test/data/file-mapping/preprocessor/File.hs create mode 100644 test/data/file-mapping/preprocessor/File_Redir.hs create mode 100644 test/data/file-mapping/preprocessor/File_Redir_Lint.hs diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index 18c583a..aec408a 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -6,6 +6,7 @@ import Test.Hspec import TestUtils import qualified Data.Map as M import Dir +import System.IO.Temp import Language.Haskell.GhcMod @@ -116,3 +117,87 @@ spec = do loadMappedFile "File.hs" (MemoryMapping $ Just "module File where\n\ntestfun = putStrLn \"Hello!\"") info "File.hs" $ Expression "testfun" res `shouldBe` "testfun :: IO () \t-- Defined at File.hs:3:1\n" + + describe "preprocessor tests" $ do + it "checks redirected file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/preprocessor" $ do + let fm = [("File.hs", RedirectedMapping "File_Redir.hs")] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + checkSyntax ["File.hs"] + res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "checks in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/preprocessor" $ do + src <- readFile "File_Redir.hs" + let fm = [("File.hs", MemoryMapping $ Just src)] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + checkSyntax ["File.hs"] + res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "lints redirected file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/preprocessor" $ do + res <- runD $ do + loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + lint "File.hs" + res `shouldBe` "File.hs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" + it "lints in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/preprocessor" $ do + src <- readFile "File_Redir_Lint.hs" + res <- runD $ do + loadMappedFile "File.hs" (MemoryMapping $ Just src) + lint "File.hs" + res `shouldBe` "File.hs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" + describe "literate haskell tests" $ do + it "checks redirected file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/lhs" $ do + let fm = [("File.lhs", RedirectedMapping "File_Redir.lhs")] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + checkSyntax ["File.lhs"] + res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "checks in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/lhs" $ do + src <- readFile "File_Redir.lhs" + let fm = [("File.lhs", MemoryMapping $ Just src)] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + checkSyntax ["File.lhs"] + res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" + -- NOTE: There is a bug in hlint that prevents it from linting lhs files. + -- it "lints redirected file if one is specified and outputs original filename" $ do + -- withDirectory_ "test/data/file-mapping/lhs" $ do + -- res <- runD $ do + -- loadMappedFile "File.lhs" (RedirectedMapping "File_Redir_Lint.lhs") + -- lint "File.lhs" + -- res `shouldBe` "File.lhs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" + -- it "lints in-memory file if one is specified and outputs original filename" $ do + -- withDirectory_ "test/data/file-mapping/lhs" $ do + -- src <- readFile "File_Redir_Lint.lhs" + -- res <- runD $ do + -- loadMappedFile "File.lhs" (MemoryMapping $ Just src) + -- lint "File.lhs" + -- res `shouldBe` "File.lhs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" + describe "template haskell" $ do + it "works with a redirected module using TemplateHaskell" $ do + withSystemTempDirectory "ghc-mod-test" $ \tmpdir -> do + srcFoo <- readFile "test/data/template-haskell/Foo.hs" + srcBar <- readFile "test/data/template-haskell/Bar.hs" + withDirectory_ "test/data/file-mapping" $ do + writeFile (tmpdir "Foo_Redir.hs") srcFoo + writeFile (tmpdir "Bar_Redir.hs") srcBar + let fm = [("Foo.hs", RedirectedMapping $ tmpdir "Foo_Redir.hs") + ,("Bar.hs", RedirectedMapping $ tmpdir "Bar_Redir.hs")] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + types "Bar.hs" 5 1 + res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] + it "works with a memory module using TemplateHaskell" $ do + srcFoo <- readFile "test/data/template-haskell/Foo.hs" + srcBar <- readFile "test/data/template-haskell/Bar.hs" + withDirectory_ "test/data/file-mapping" $ do + let fm = [("Foo.hs", MemoryMapping $ Just srcFoo) + ,("Bar.hs", MemoryMapping $ Just srcBar)] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFile) fm + types "Bar.hs" 5 1 + res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] diff --git a/test/data/file-mapping/lhs/File.lhs b/test/data/file-mapping/lhs/File.lhs new file mode 100644 index 0000000..3ec4635 --- /dev/null +++ b/test/data/file-mapping/lhs/File.lhs @@ -0,0 +1,2 @@ +> main :: IO () +> main = putStrLn "Hello World!" diff --git a/test/data/file-mapping/lhs/File_Redir.lhs b/test/data/file-mapping/lhs/File_Redir.lhs new file mode 100644 index 0000000..26d462c --- /dev/null +++ b/test/data/file-mapping/lhs/File_Redir.lhs @@ -0,0 +1 @@ +> main = putStrLn "Hello World!" diff --git a/test/data/file-mapping/lhs/File_Redir_Lint.lhs b/test/data/file-mapping/lhs/File_Redir_Lint.lhs new file mode 100644 index 0000000..66bf9ca --- /dev/null +++ b/test/data/file-mapping/lhs/File_Redir_Lint.lhs @@ -0,0 +1,4 @@ +> module File where + +> func :: Num a => a -> a -> a +> func a b = (*) a b diff --git a/test/data/file-mapping/preprocessor/File.hs b/test/data/file-mapping/preprocessor/File.hs new file mode 100644 index 0000000..41d1be2 --- /dev/null +++ b/test/data/file-mapping/preprocessor/File.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE CPP #-} +#ifndef NOTHING +main :: IO () +main = putStrLn "Hello World!" +#else +INVALID +#endif diff --git a/test/data/file-mapping/preprocessor/File_Redir.hs b/test/data/file-mapping/preprocessor/File_Redir.hs new file mode 100644 index 0000000..ca07e79 --- /dev/null +++ b/test/data/file-mapping/preprocessor/File_Redir.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE CPP #-} +#ifndef NOTHING +main = putStrLn "Hello World!" +#else +INVALID +#endif diff --git a/test/data/file-mapping/preprocessor/File_Redir_Lint.hs b/test/data/file-mapping/preprocessor/File_Redir_Lint.hs new file mode 100644 index 0000000..8f8e298 --- /dev/null +++ b/test/data/file-mapping/preprocessor/File_Redir_Lint.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE CPP #-} +#ifndef NOTHING +module File where + +func :: Num a => a -> a -> a +func a b = (*) a b +#else +INVALID +#endif From 654b172f5ed4c002b039090a0172d1d07a49ff11 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 18:22:27 +0300 Subject: [PATCH 031/147] Add FileMapping support to HMG/preprocessFile Post-rebase update --- Language/Haskell/GhcMod/HomeModuleGraph.hs | 16 +++++++++++++++- Language/Haskell/GhcMod/Utils.hs | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index f9e6577..1272f4e 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -54,12 +54,15 @@ import Data.Set (Set) import qualified Data.Set as Set import System.FilePath import System.Directory +import System.IO +import System.IO.Temp import Prelude import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Utils (getMappedFileSource) import Language.Haskell.GhcMod.Gap (parseModuleHeader) -- | Turn module graph into a graphviz dot file @@ -244,8 +247,19 @@ preprocessFile :: (IOish m, GmEnv m, GmState m) => HscEnv -> FilePath -> m (Either [String] ([String], (DynFlags, FilePath))) preprocessFile env file = withLogger' env $ \setDf -> do + src <- runMaybeT $ getMappedFileSource file let env' = env { hsc_dflags = setDf (hsc_dflags env) } - liftIO $ preprocess env' (file, Nothing) + maybe + (liftIO $ preprocess env' (file, Nothing)) + (preprocessWithTemp env' file) + src + where + preprocessWithTemp env' fn src = do + tmpdir <- cradleTempDir <$> cradle + liftIO $ withTempFile tmpdir fn $ \fn' hndl -> do + hPutStr hndl src + hClose hndl + preprocess env' (fn', Nothing) fileModuleName :: (IOish m, GmEnv m, GmState m) => HscEnv -> FilePath -> m (Either [String] (Maybe ModuleName)) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index c4f2710..74b3e49 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -44,6 +44,7 @@ import Text.Printf import Paths_ghc_mod (getLibexecDir) import Utils import Prelude +import Control.Monad.Trans.Maybe -- dropWhileEnd is not provided prior to base 4.5.0.0. dropWhileEnd :: (a -> Bool) -> [a] -> [a] @@ -186,6 +187,14 @@ getCanonicalFileNameSafe fn = do then liftIO $ canonicalizePath ccfn else return ccfn +getMappedFileSource :: (IOish m, GmEnv m, GmState m) => FilePath -> MaybeT m String +getMappedFileSource fn = do + mf <- MaybeT $ getCanonicalFileNameSafe fn >>= lookupMMappedFile + case mf of + RedirectedMapping fn' -> liftIO $ readFile fn' + MemoryMapping (Just src) -> return src + _ -> mzero + mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath) mkRevRedirMapFunc = do rm <- M.fromList <$> mapMaybe (uncurry mf) <$> M.toList <$> getMMappedFiles From 9a22662031471592fafbd4991ccabe7ac62ba5c2 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 18:49:48 +0300 Subject: [PATCH 032/147] Update in-memory preprocessing check spec --- test/FileMappingSpec.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index aec408a..71bd81d 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -126,14 +126,14 @@ spec = do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" - it "checks in-memory file if one is specified and outputs original filename" $ do + it "doesn't check in-memory file" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do src <- readFile "File_Redir.hs" let fm = [("File.hs", MemoryMapping $ Just src)] res <- run defaultOptions $ do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] - res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + res `shouldBe` "buffer needs preprocesing; interactive check disabled\n" it "lints redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do res <- runD $ do From 20d6d4bae7ab6d32c7eb043a57f1df3785867da7 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 19:15:32 +0300 Subject: [PATCH 033/147] Disable lhs MemoryMapped test for now --- test/FileMappingSpec.hs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index 71bd81d..98ad727 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -155,14 +155,15 @@ spec = do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.lhs"] res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" - it "checks in-memory file if one is specified and outputs original filename" $ do - withDirectory_ "test/data/file-mapping/lhs" $ do - src <- readFile "File_Redir.lhs" - let fm = [("File.lhs", MemoryMapping $ Just src)] - res <- run defaultOptions $ do - mapM_ (uncurry loadMappedFile) fm - checkSyntax ["File.lhs"] - res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" + -- NOTE: GHC can't 'unliterate' a file in-memory, so this won't work + -- it "checks in-memory file if one is specified and outputs original filename" $ do + -- withDirectory_ "test/data/file-mapping/lhs" $ do + -- src <- readFile "File_Redir.lhs" + -- let fm = [("File.lhs", MemoryMapping $ Just src)] + -- res <- run defaultOptions $ do + -- mapM_ (uncurry loadMappedFile) fm + -- checkSyntax ["File.lhs"] + -- res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" -- NOTE: There is a bug in hlint that prevents it from linting lhs files. -- it "lints redirected file if one is specified and outputs original filename" $ do -- withDirectory_ "test/data/file-mapping/lhs" $ do From a5dae2a82db189c20fccbd33180bd91b74d7e583 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 23:20:00 +0300 Subject: [PATCH 034/147] Drop memory-mapped files, since ghc doesn't play well with those All files are now "redirected", either user-created, or created by ghc-mod itself. --- Language/Haskell/GhcMod.hs | 1 + Language/Haskell/GhcMod/FileMapping.hs | 63 ++++++----- Language/Haskell/GhcMod/HomeModuleGraph.hs | 28 ++--- Language/Haskell/GhcMod/Logger.hs | 8 +- Language/Haskell/GhcMod/Target.hs | 2 +- Language/Haskell/GhcMod/Types.hs | 5 +- Language/Haskell/GhcMod/Utils.hs | 36 ++----- src/GHCMod.hs | 18 ++-- test/FileMappingSpec.hs | 118 ++++++++++++++------- 9 files changed, 151 insertions(+), 128 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index b9e7050..a6a555b 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -66,6 +66,7 @@ module Language.Haskell.GhcMod ( , gmUnsafeErrStrLn -- * FileMapping , loadMappedFile + , loadMappedFileSource , unloadMappedFile ) where diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index baca49b..656cea0 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -1,5 +1,6 @@ module Language.Haskell.GhcMod.FileMapping ( loadMappedFile + , loadMappedFileSource , unloadMappedFile , mapFile , fileModSummaryWithMapping @@ -11,43 +12,55 @@ import Language.Haskell.GhcMod.Gap import Language.Haskell.GhcMod.HomeModuleGraph import Language.Haskell.GhcMod.Utils -import Data.Time +import System.IO +import System.FilePath +import System.Directory import Control.Monad.Trans.Maybe import GHC +import Control.Monad +import Control.Monad.Trans (lift) -loadMappedFile :: IOish m => FilePath -> FileMapping -> GhcModT m () -loadMappedFile from fm = - getCanonicalFileNameSafe from >>= (`addMMappedFile` fm) +loadMappedFile :: IOish m => FilePath -> FilePath -> GhcModT m () +loadMappedFile from to = + getCanonicalFileNameSafe from >>= (`addMMappedFile` FileMapping to False) -mapFile :: (IOish m, GmState m, GhcMonad m) => +loadMappedFileSource :: IOish m => FilePath -> String -> GhcModT m () +loadMappedFileSource from src = do + tmpdir <- cradleTempDir `fmap` cradle + to <- liftIO $ do + (fn, h) <- openTempFile tmpdir (takeFileName from) + hPutStr h src + hClose h + return fn + getCanonicalFileNameSafe from >>= (`addMMappedFile` FileMapping to True) + +mapFile :: (IOish m, GmState m, GhcMonad m, GmEnv m) => HscEnv -> Target -> m Target mapFile _ (Target tid@(TargetFile filePath _) taoc _) = do mapping <- lookupMMappedFile filePath - mkMappedTarget tid taoc mapping + mkMappedTarget (Just filePath) tid taoc mapping mapFile env (Target tid@(TargetModule moduleName) taoc _) = do - mapping <- runMaybeT $ do - filePath <- MaybeT $ liftIO $ findModulePath env moduleName - MaybeT $ lookupMMappedFile $ mpPath filePath - mkMappedTarget tid taoc mapping + (fp, mapping) <- do + filePath <- fmap (fmap mpPath) (liftIO $ findModulePath env moduleName) + mmf <- runMaybeT $ MaybeT (return filePath) >>= MaybeT . lookupMMappedFile + return (filePath, mmf) + mkMappedTarget fp tid taoc mapping -mkMappedTarget :: (IOish m, GmState m, GhcMonad m) => - TargetId -> Bool -> Maybe FileMapping -> m Target -mkMappedTarget _ taoc (Just (RedirectedMapping to)) = - return $ mkTarget (TargetFile to Nothing) taoc Nothing -mkMappedTarget tid taoc (Just (MemoryMapping (Just src))) = do - sb <- toStringBuffer [src] - ct <- liftIO getCurrentTime - return $ mkTarget tid taoc $ Just (sb, ct) -mkMappedTarget tid taoc _ = return $ mkTarget tid taoc Nothing +mkMappedTarget :: (IOish m, GmState m, GmEnv m, GhcMonad m) => + Maybe FilePath -> TargetId -> Bool -> Maybe FileMapping -> m Target +mkMappedTarget _ _ taoc (Just to) = + return $ mkTarget (TargetFile (fmPath to) Nothing) taoc Nothing +mkMappedTarget _ tid taoc _ = return $ mkTarget tid taoc Nothing unloadMappedFile :: IOish m => FilePath -> GhcModT m () -unloadMappedFile = (delMMappedFile =<<) . getCanonicalFileNameSafe +unloadMappedFile what = void $ runMaybeT $ do + cfn <- lift $ getCanonicalFileNameSafe what + fm <- MaybeT $ lookupMMappedFile cfn + liftIO $ when (fmTemp fm) $ removeFile (fmPath fm) + delMMappedFile cfn fileModSummaryWithMapping :: (IOish m, GmState m, GhcMonad m, GmEnv m) => FilePath -> m ModSummary -fileModSummaryWithMapping fn = do - mmf <- getCanonicalFileNameSafe fn >>= lookupMMappedFile - case mmf of - Just (RedirectedMapping to) -> fileModSummary to - _ -> fileModSummary fn +fileModSummaryWithMapping fn = + withMappedFile fn $ \fn' -> fileModSummary fn' diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index 1272f4e..a6ae1f3 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -44,6 +44,7 @@ import Control.Arrow ((&&&)) import Control.Applicative import Control.Monad import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) +import Control.Monad.Trans (lift) import Control.Monad.State.Strict (execStateT) import Control.Monad.State.Class import Data.Maybe @@ -62,7 +63,7 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Logger import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types -import Language.Haskell.GhcMod.Utils (getMappedFileSource) +import Language.Haskell.GhcMod.Utils (withMappedFile) import Language.Haskell.GhcMod.Gap (parseModuleHeader) -- | Turn module graph into a graphviz dot file @@ -247,19 +248,9 @@ preprocessFile :: (IOish m, GmEnv m, GmState m) => HscEnv -> FilePath -> m (Either [String] ([String], (DynFlags, FilePath))) preprocessFile env file = withLogger' env $ \setDf -> do - src <- runMaybeT $ getMappedFileSource file - let env' = env { hsc_dflags = setDf (hsc_dflags env) } - maybe - (liftIO $ preprocess env' (file, Nothing)) - (preprocessWithTemp env' file) - src - where - preprocessWithTemp env' fn src = do - tmpdir <- cradleTempDir <$> cradle - liftIO $ withTempFile tmpdir fn $ \fn' hndl -> do - hPutStr hndl src - hClose hndl - preprocess env' (fn', Nothing) + withMappedFile file $ \fn -> do + let env' = env { hsc_dflags = setDf (hsc_dflags env) } + liftIO $ preprocess env' (fn, Nothing) fileModuleName :: (IOish m, GmEnv m, GmState m) => HscEnv -> FilePath -> m (Either [String] (Maybe ModuleName)) @@ -269,11 +260,12 @@ fileModuleName env fn = do case ep of Left errs -> do return $ Left errs - Right (_warns, (dflags, procdFile)) -> handler $ do + Right (_warns, (dflags, procdFile)) -> leftM (errBagToStrList env) =<< handler (do src <- readFile procdFile case parseModuleHeader src dflags procdFile of - Left errs -> do - return $ Left $ errBagToStrList env errs + Left errs -> return $ Left errs Right (_, lmdl) -> do let HsModule {..} = unLoc lmdl - return $ Right $ unLoc <$> hsmodName + return $ Right $ unLoc <$> hsmodName) + where + leftM f = either (return . Left <=< f) (return . Right) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 57fc650..5423c52 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -108,12 +108,16 @@ withLogger' env action = do return ((,) ls <$> a) -errBagToStrList :: HscEnv -> Bag ErrMsg -> [String] +errBagToStrList :: (Functor m, GmState m, GmEnv m) => HscEnv -> Bag ErrMsg -> m [String] errBagToStrList env errs = let dflags = hsc_dflags env pu = icPrintUnqual dflags (hsc_IC env) st = mkUserStyle pu AllTheWay - in runReader (errsToStr (bagToList errs)) GmPprEnv{gpeDynFlags=dflags, gpePprStyle=st} + in do + rfm <- mkRevRedirMapFunc + return $ runReader + (errsToStr (bagToList errs)) + GmPprEnv{gpeDynFlags=dflags, gpePprStyle=st, gpeMapFile=rfm} ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 5cd5b94..ffdcd1f 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -151,7 +151,7 @@ runGmlTWith efnmns' mdf wrapper action = do let (fns, mns) = partitionEithers efnmns' ccfns = map (cradleCurrentDir crdl ) fns - cfns <- liftIO $ mapM canonicalizePath ccfns + cfns <- mapM getCanonicalFileNameSafe ccfns let serfnmn = Set.fromList $ map Right mns ++ map Left cfns opts <- targetGhcOptions crdl serfnmn let opts' = opts ++ ["-O0"] ++ ghcUserOptions diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 029cd23..b632b2a 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -69,8 +69,7 @@ data OutputStyle = LispStyle -- ^ S expression style. -- | The type for line separator. Historically, a Null string is used. newtype LineSeparator = LineSeparator String deriving (Show) -data FileMapping = RedirectedMapping FilePath - | MemoryMapping (Maybe String) +data FileMapping = FileMapping {fmPath :: FilePath, fmTemp :: Bool} deriving Show type FileMappingMap = Map FilePath FileMapping @@ -99,7 +98,7 @@ data Options = Options { -- | If 'True', 'browse' will return fully qualified name , qualified :: Bool , hlintOpts :: [String] - , fileMappings :: [(FilePath,FileMapping)] + , fileMappings :: [(FilePath, Maybe FilePath)] } deriving (Show) -- | A default 'Options'. diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 74b3e49..b675c06 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -26,25 +26,23 @@ module Language.Haskell.GhcMod.Utils ( import Control.Applicative import Data.Char import qualified Data.Map as M -import Data.Maybe (mapMaybe, fromMaybe) +import Data.Maybe (fromMaybe) import Exception import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist, - getTemporaryDirectory, canonicalizePath, removeFile) + getTemporaryDirectory, canonicalizePath) import System.Environment import System.FilePath (splitDrive, takeDirectory, takeFileName, pathSeparators, (), makeRelative) -import System.IO.Temp (createTempDirectory, openTempFile) -import System.IO (hPutStr, hClose) +import System.IO.Temp (createTempDirectory) import System.Process (readProcess) import Text.Printf import Paths_ghc_mod (getLibexecDir) import Utils import Prelude -import Control.Monad.Trans.Maybe -- dropWhileEnd is not provided prior to base 4.5.0.0. dropWhileEnd :: (a -> Bool) -> [a] -> [a] @@ -167,44 +165,26 @@ withMappedFile :: (IOish m, GmState m, GmEnv m) => forall a. FilePath -> (FilePath -> m a) -> m a withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile >>= runWithFile where - runWithFile (Just (RedirectedMapping to)) = action to - runWithFile (Just (MemoryMapping (Just src))) = do - crdl <- cradle - (fp,hndl) <- liftIO $ openTempFile (cradleTempDir crdl) (takeFileName file) - liftIO $ hPutStr hndl src - liftIO $ hClose hndl - result <- action fp - liftIO $ removeFile fp - return result + runWithFile (Just to) = action $ fmPath to runWithFile _ = action file getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath getCanonicalFileNameSafe fn = do crdl <- cradle - let ccfn = cradleCurrentDir crdl fn + let ccfn = cradleRootDir crdl fn fex <- liftIO $ doesFileExist ccfn if fex then liftIO $ canonicalizePath ccfn else return ccfn -getMappedFileSource :: (IOish m, GmEnv m, GmState m) => FilePath -> MaybeT m String -getMappedFileSource fn = do - mf <- MaybeT $ getCanonicalFileNameSafe fn >>= lookupMMappedFile - case mf of - RedirectedMapping fn' -> liftIO $ readFile fn' - MemoryMapping (Just src) -> return src - _ -> mzero - mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath) mkRevRedirMapFunc = do - rm <- M.fromList <$> mapMaybe (uncurry mf) <$> M.toList <$> getMMappedFiles + rm <- M.fromList <$> map (uncurry mf) <$> M.toList <$> getMMappedFiles crdl <- cradle return $ \key -> fromMaybe key $ makeRelative (cradleRootDir crdl) <$> M.lookup key rm where - mf :: FilePath -> FileMapping -> Maybe (FilePath, FilePath) - mf from (RedirectedMapping to) - = Just (to, from) - mf _ _ = Nothing + mf :: FilePath -> FileMapping -> (FilePath, FilePath) + mf from to = (fmPath to, from) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index c7f5030..34663c4 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -274,8 +274,8 @@ globalArgSpec = , option "" ["map-file"] "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" $ reqArg "OPT" $ \g o -> let m = case second (drop 1) $ span (/='=') g of - (s,"") -> (s, MemoryMapping Nothing) - (f,t) -> (f, RedirectedMapping t) + (s,"") -> (s, Nothing) + (f,t) -> (f, Just t) in Right $ o { fileMappings = m : fileMappings o } @@ -359,16 +359,10 @@ main = do progMain :: (Options,[String]) -> IO () progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do - let - loadMMappedFiles from (MemoryMapping Nothing) = do - src <- liftIO getFileSourceFromStdin - return (from, MemoryMapping $ Just src) - loadMMappedFiles from x = return (from, x) - fileMappings' <- forM (reverse $ fileMappings globalOptions) $ uncurry loadMMappedFiles case globalCommands cmdArgs of Just s -> gmPutStr s Nothing -> do - mapM_ (uncurry loadMappedFile) fileMappings' + forM_ (reverse $ fileMappings globalOptions) $ uncurry loadMMappedFiles ghcCommands cmdArgs where hndle action = do @@ -378,6 +372,10 @@ progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ return () Left ed -> exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc ed) + loadMMappedFiles from (Just to) = loadMappedFile from to + loadMMappedFiles from (Nothing) = do + src <- liftIO getFileSourceFromStdin + loadMappedFileSource from src globalCommands :: [String] -> Maybe String globalCommands (cmd:_) @@ -447,7 +445,7 @@ legacyInteractiveLoop symdbreq world = do "browse" -> browseCmd args "map-file" -> liftIO getFileSourceFromStdin - >>= loadMappedFile arg . MemoryMapping . Just + >>= loadMappedFileSource arg >> return "" "unmap-file" -> unloadMappedFile arg diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index 98ad727..8cfe2b0 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -7,6 +7,7 @@ import TestUtils import qualified Data.Map as M import Dir import System.IO.Temp +import System.Directory import Language.Haskell.GhcMod @@ -16,45 +17,74 @@ spec = do it "inserts a given FilePath FileMapping into state with canonicalized path" $ do withDirectory_ "test/data/file-mapping" $ do mappedFiles <- runD $ do - loadMappedFile "File.hs" (MemoryMapping Nothing) + loadMappedFile "File.hs" "File.hs" getMMappedFiles dir <- getCurrentDirectory - show mappedFiles `shouldBe` show (M.fromList [(dir "File.hs", MemoryMapping Nothing)]) + show mappedFiles `shouldBe` show (M.fromList [(dir "File.hs", FileMapping "File.hs" False)]) it "should try to guess a canonical name if file doesn't exist" $ do withDirectory_ "test/data/file-mapping" $ do mappedFiles <- runD $ do - loadMappedFile "NonExistantFile.hs" (MemoryMapping Nothing) + loadMappedFile "NonExistantFile.hs" "File.hs" getMMappedFiles dir <- getCurrentDirectory - show mappedFiles `shouldBe` show (M.fromList [(dir "NonExistantFile.hs", MemoryMapping Nothing)]) + show mappedFiles `shouldBe` show (M.fromList [(dir "NonExistantFile.hs", FileMapping "File.hs" False)]) + + describe "loadMappedFileSource" $ do + it "inserts a given FilePath FileMapping into state with canonicalized path" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFileSource "File.hs" "main :: IO ()" + getMMappedFiles + dir <- getCurrentDirectory + -- TODO + M.toList mappedFiles `shouldSatisfy` \[(fn, FileMapping _to True)] -> + fn == dir "File.hs" + it "should try to guess a canonical name if file doesn't exist" $ do + withDirectory_ "test/data/file-mapping" $ do + mappedFiles <- runD $ do + loadMappedFileSource "NonExistantFile.hs" "main :: IO ()" + getMMappedFiles + dir <- getCurrentDirectory + -- TODO + M.toList mappedFiles `shouldSatisfy` \[(fn, FileMapping _to True)] -> + fn == dir "NonExistantFile.hs" describe "unloadMappedFile" $ do it "removes a given FilePath from state" $ do withDirectory_ "test/data/file-mapping" $ do mappedFiles <- runD $ do - loadMappedFile "File.hs" (MemoryMapping Nothing) + loadMappedFile "File.hs" "File2.hs" unloadMappedFile "File.hs" getMMappedFiles show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)])) it "should work even if file does not exist" $ do withDirectory_ "test/data/file-mapping" $ do mappedFiles <- runD $ do - loadMappedFile "NonExistantFile.hs" (MemoryMapping Nothing) + loadMappedFile "NonExistantFile.hs" "File2.hs" unloadMappedFile "NonExistantFile.hs" getMMappedFiles show mappedFiles `shouldBe` show (M.fromList ([] :: [(FilePath, FileMapping)])) + it "should remove created temporary files" $ do + withDirectory_ "test/data/file-mapping" $ do + dir <- getCurrentDirectory + fileExists <- runD $ do + loadMappedFileSource "NonExistantFile.hs" "main :: IO ()" + fp <- maybe undefined fmPath `fmap` lookupMMappedFile (dir "NonExistantFile.hs") + unloadMappedFile "NonExistantFile.hs" + liftIO $ doesFileExist fp + not fileExists `shouldBe` True describe "withMappedFile" $ do it "checks if there is a redirected file and calls and action with its FilePath" $ do withDirectory_ "test/data/file-mapping" $ do res <- runD $ do - loadMappedFile "File.hs" (RedirectedMapping "File_Redir.hs") + loadMappedFile "File.hs" "File_Redir.hs" withMappedFile "File.hs" return res `shouldBe` "File_Redir.hs" it "checks if there is an in-memory file and calls and action with temporary file" $ do withDirectory_ "test/data/file-mapping" $ do (fn, src) <- runD $ do - loadMappedFile "File.hs" (MemoryMapping $ Just "main = test") + loadMappedFileSource "File.hs" "main = test" withMappedFile "File.hs" $ \fn -> do src <- liftIO $ readFile fn return (fn, src) @@ -69,101 +99,107 @@ spec = do describe "integration tests" $ do it "checks redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do - let fm = [("File.hs", RedirectedMapping "File_Redir.hs")] + let fm = [("File.hs", "File_Redir.hs")] res <- run defaultOptions $ do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "checks in-memory file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do - let fm = [("File.hs", MemoryMapping $ Just "main = putStrLn \"Hello World!\"\n")] + let fm = [("File.hs", "main = putStrLn \"Hello World!\"\n")] res <- run defaultOptions $ do - mapM_ (uncurry loadMappedFile) fm + mapM_ (uncurry loadMappedFileSource) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "should work even if file doesn't exist" $ do + withDirectory_ "test/data/file-mapping" $ do + let fm = [("Nonexistent.hs", "main = putStrLn \"Hello World!\"\n")] + res <- run defaultOptions{logLevel=GmDebug} $ do + mapM_ (uncurry loadMappedFileSource) fm + checkSyntax ["Nonexistent.hs"] + res `shouldBe` "Nonexistent.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "lints redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do res <- runD $ do - loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + loadMappedFile "File.hs" "File_Redir_Lint.hs" lint "File.hs" res `shouldBe` "File.hs:4:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" it "lints in-memory file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping" $ do res <- runD $ do - loadMappedFile "File.hs" (MemoryMapping $ Just "func a b = (++) a b\n") + loadMappedFileSource "File.hs" "func a b = (++) a b\n" lint "File.hs" res `shouldBe` "File.hs:1:1: Error: Eta reduce\NULFound:\NUL func a b = (++) a b\NULWhy not:\NUL func = (++)\n" it "shows types of the expression for redirected files" $ do let tdir = "test/data/file-mapping" res <- runD' tdir $ do - loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + loadMappedFile "File.hs" "File_Redir_Lint.hs" types "File.hs" 4 12 res `shouldBe` "4 12 4 15 \"a -> a -> a\"\n4 12 4 17 \"a -> a\"\n4 12 4 19 \"a\"\n4 1 4 19 \"a -> a -> a\"\n" it "shows types of the expression for in-memory files" $ do let tdir = "test/data/file-mapping" res <- runD' tdir $ do - loadMappedFile "File.hs" (MemoryMapping $ Just "main = putStrLn \"Hello!\"") + loadMappedFileSource "File.hs" "main = putStrLn \"Hello!\"" types "File.hs" 1 14 res `shouldBe` "1 8 1 16 \"String -> IO ()\"\n1 8 1 25 \"IO ()\"\n1 1 1 25 \"IO ()\"\n" it "shows info for the expression for redirected files" $ do let tdir = "test/data/file-mapping" res <- runD' tdir $ do - loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + loadMappedFile "File.hs" "File_Redir_Lint.hs" info "File.hs" $ Expression "func" res `shouldBe` "func :: Num a => a -> a -> a \t-- Defined at File.hs:4:1\n" it "shows info for the expression for in-memory files" $ do let tdir = "test/data/file-mapping" res <- runD' tdir $ do - loadMappedFile "File.hs" (MemoryMapping $ Just "module File where\n\ntestfun = putStrLn \"Hello!\"") + loadMappedFileSource "File.hs" "module File where\n\ntestfun = putStrLn \"Hello!\"" info "File.hs" $ Expression "testfun" res `shouldBe` "testfun :: IO () \t-- Defined at File.hs:3:1\n" describe "preprocessor tests" $ do it "checks redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do - let fm = [("File.hs", RedirectedMapping "File_Redir.hs")] + let fm = [("File.hs", "File_Redir.hs")] res <- run defaultOptions $ do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.hs"] res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" - it "doesn't check in-memory file" $ do + it "checks in-memory file" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do src <- readFile "File_Redir.hs" - let fm = [("File.hs", MemoryMapping $ Just src)] + let fm = [("File.hs", src)] res <- run defaultOptions $ do - mapM_ (uncurry loadMappedFile) fm + mapM_ (uncurry loadMappedFileSource) fm checkSyntax ["File.hs"] - res `shouldBe` "buffer needs preprocesing; interactive check disabled\n" + res `shouldBe` "File.hs:3:1:Warning: Top-level binding with no type signature: main :: IO ()\n" it "lints redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do res <- runD $ do - loadMappedFile "File.hs" (RedirectedMapping "File_Redir_Lint.hs") + loadMappedFile "File.hs" "File_Redir_Lint.hs" lint "File.hs" res `shouldBe` "File.hs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" it "lints in-memory file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping/preprocessor" $ do src <- readFile "File_Redir_Lint.hs" res <- runD $ do - loadMappedFile "File.hs" (MemoryMapping $ Just src) + loadMappedFileSource "File.hs" src lint "File.hs" res `shouldBe` "File.hs:6:1: Error: Eta reduce\NULFound:\NUL func a b = (*) a b\NULWhy not:\NUL func = (*)\n" describe "literate haskell tests" $ do it "checks redirected file if one is specified and outputs original filename" $ do withDirectory_ "test/data/file-mapping/lhs" $ do - let fm = [("File.lhs", RedirectedMapping "File_Redir.lhs")] + let fm = [("File.lhs", "File_Redir.lhs")] res <- run defaultOptions $ do mapM_ (uncurry loadMappedFile) fm checkSyntax ["File.lhs"] res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" - -- NOTE: GHC can't 'unliterate' a file in-memory, so this won't work - -- it "checks in-memory file if one is specified and outputs original filename" $ do - -- withDirectory_ "test/data/file-mapping/lhs" $ do - -- src <- readFile "File_Redir.lhs" - -- let fm = [("File.lhs", MemoryMapping $ Just src)] - -- res <- run defaultOptions $ do - -- mapM_ (uncurry loadMappedFile) fm - -- checkSyntax ["File.lhs"] - -- res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" + it "checks in-memory file if one is specified and outputs original filename" $ do + withDirectory_ "test/data/file-mapping/lhs" $ do + src <- readFile "File_Redir.lhs" + let fm = [("File.lhs", src)] + res <- run defaultOptions $ do + mapM_ (uncurry loadMappedFileSource) fm + checkSyntax ["File.lhs"] + res `shouldBe` "File.lhs:1:3:Warning: Top-level binding with no type signature: main :: IO ()\n" -- NOTE: There is a bug in hlint that prevents it from linting lhs files. -- it "lints redirected file if one is specified and outputs original filename" $ do -- withDirectory_ "test/data/file-mapping/lhs" $ do @@ -186,9 +222,9 @@ spec = do withDirectory_ "test/data/file-mapping" $ do writeFile (tmpdir "Foo_Redir.hs") srcFoo writeFile (tmpdir "Bar_Redir.hs") srcBar - let fm = [("Foo.hs", RedirectedMapping $ tmpdir "Foo_Redir.hs") - ,("Bar.hs", RedirectedMapping $ tmpdir "Bar_Redir.hs")] - res <- run defaultOptions $ do + let fm = [("Foo.hs", tmpdir "Foo_Redir.hs") + ,("Bar.hs", tmpdir "Bar_Redir.hs")] + res <- run defaultOptions{logLevel = GmDebug} $ do mapM_ (uncurry loadMappedFile) fm types "Bar.hs" 5 1 res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] @@ -196,9 +232,9 @@ spec = do srcFoo <- readFile "test/data/template-haskell/Foo.hs" srcBar <- readFile "test/data/template-haskell/Bar.hs" withDirectory_ "test/data/file-mapping" $ do - let fm = [("Foo.hs", MemoryMapping $ Just srcFoo) - ,("Bar.hs", MemoryMapping $ Just srcBar)] - res <- run defaultOptions $ do - mapM_ (uncurry loadMappedFile) fm + let fm = [("Foo.hs", srcFoo) + ,("Bar.hs", srcBar)] + res <- run defaultOptions{logLevel = GmDebug} $ do + mapM_ (uncurry loadMappedFileSource) fm types "Bar.hs" 5 1 res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] From e0044a3697b4ffa8c454c93c93711a3b1c0cc791 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 23:28:45 +0300 Subject: [PATCH 035/147] Add FileMapping commands description to ghc-mod main. --- src/GHCMod.hs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 34663c4..44a6e30 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -271,6 +271,40 @@ globalArgSpec = reqArg "OPT" $ \g o -> Right $ o { ghcUserOptions = g : ghcUserOptions o } +{- +File map docs: + +CLI options: +* `--map-file "file1.hs=file2.hs"` can be used to tell + ghc-mod that it should take source code for `file1.hs` from `file2.hs`. + `file1.hs` can be either full path, or path relative to project root. + `file2.hs` has to be either relative to current directory, + or full path (preferred). +* `--map-file "file.hs"` can be used to tell ghc-mod that it should take + source code for `file.hs` from stdin. File end marker is `\EOT\n`, + i.e. `\x04\x0A`. `file.hs` may or may not exist, and should be + either full path, or relative to project root. + +Interactive commands: +* `map-file file.hs` -- tells ghc-modi to read `file.hs` source from stdin. + Works the same as second form of `--map-file` CLI option. +* `unmap-file file.hs` -- unloads previously mapped file, so that it's + no longer mapped. `file.hs` can be full path or relative to + project root, either will work. + +Exposed functions: +* `loadMappedFile :: FilePath -> FilePath -> GhcModT m ()` -- maps `FilePath`, + given as first argument to take source from `FilePath` given as second + argument. Works exactly the same as first form of `--map-file` + CLI option. +* `loadMappedFileSource :: FilePath -> String -> GhcModT m ()` -- maps + `FilePath`, given as first argument to have source as given + by second argument. Works exactly the same as second form of `--map-file` + CLI option, sans reading from stdin. +* `unloadMappedFile :: FilePath -> GhcModT m ()` -- unmaps `FilePath`, given as + first argument, and removes any temporary files created when file was + mapped. Works exactly the same as `unmap-file` interactive command +-} , option "" ["map-file"] "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" $ reqArg "OPT" $ \g o -> let m = case second (drop 1) $ span (/='=') g of From 8ef8a86397bf225ef17024b744118802162211f2 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 23:38:32 +0300 Subject: [PATCH 036/147] Unload mapped files before loading --- Language/Haskell/GhcMod/FileMapping.hs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/FileMapping.hs b/Language/Haskell/GhcMod/FileMapping.hs index 656cea0..c9490e6 100644 --- a/Language/Haskell/GhcMod/FileMapping.hs +++ b/Language/Haskell/GhcMod/FileMapping.hs @@ -19,11 +19,9 @@ import System.Directory import Control.Monad.Trans.Maybe import GHC import Control.Monad -import Control.Monad.Trans (lift) loadMappedFile :: IOish m => FilePath -> FilePath -> GhcModT m () -loadMappedFile from to = - getCanonicalFileNameSafe from >>= (`addMMappedFile` FileMapping to False) +loadMappedFile from to = loadMappedFile' from to False loadMappedFileSource :: IOish m => FilePath -> String -> GhcModT m () loadMappedFileSource from src = do @@ -33,7 +31,13 @@ loadMappedFileSource from src = do hPutStr h src hClose h return fn - getCanonicalFileNameSafe from >>= (`addMMappedFile` FileMapping to True) + loadMappedFile' from to True + +loadMappedFile' :: IOish m => FilePath -> FilePath -> Bool -> GhcModT m () +loadMappedFile' from to isTemp = do + cfn <- getCanonicalFileNameSafe from + unloadMappedFile' cfn + addMMappedFile cfn (FileMapping to isTemp) mapFile :: (IOish m, GmState m, GhcMonad m, GmEnv m) => HscEnv -> Target -> m Target @@ -54,8 +58,10 @@ mkMappedTarget _ _ taoc (Just to) = mkMappedTarget _ tid taoc _ = return $ mkTarget tid taoc Nothing unloadMappedFile :: IOish m => FilePath -> GhcModT m () -unloadMappedFile what = void $ runMaybeT $ do - cfn <- lift $ getCanonicalFileNameSafe what +unloadMappedFile = getCanonicalFileNameSafe >=> unloadMappedFile' + +unloadMappedFile' :: IOish m => FilePath -> GhcModT m () +unloadMappedFile' cfn = void $ runMaybeT $ do fm <- MaybeT $ lookupMMappedFile cfn liftIO $ when (fmTemp fm) $ removeFile (fmPath fm) delMMappedFile cfn From ab19da08db94c11c0dac2442df7523bb38418136 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Sun, 16 Aug 2015 23:42:20 +0300 Subject: [PATCH 037/147] Removed redundant imports --- Language/Haskell/GhcMod/HomeModuleGraph.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index a6ae1f3..c9d103d 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -44,7 +44,6 @@ import Control.Arrow ((&&&)) import Control.Applicative import Control.Monad import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) -import Control.Monad.Trans (lift) import Control.Monad.State.Strict (execStateT) import Control.Monad.State.Class import Data.Maybe @@ -56,7 +55,6 @@ import qualified Data.Set as Set import System.FilePath import System.Directory import System.IO -import System.IO.Temp import Prelude import Language.Haskell.GhcMod.Logging From f4aea2c08acec7dc2c1ee64bfa4b6133574c95eb Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Mon, 17 Aug 2015 00:26:43 +0300 Subject: [PATCH 038/147] Add new test-files to .cabal --- ghc-mod.cabal | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 357f55e..105a1f5 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -82,6 +82,8 @@ Extra-Source-Files: ChangeLog test/data/cabal-preprocessors/*.hs test/data/cabal-preprocessors/*.hsc test/data/file-mapping/*.hs + test/data/file-mapping/preprocessor/*.hs + test/data/file-mapping/lhs/*.lhs Library Default-Language: Haskell2010 From 1f14ff08c6190d033ac836c17f64c5699da7dfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 17 Aug 2015 04:58:33 +0200 Subject: [PATCH 039/147] Log exceptions as GmException --- Language/Haskell/GhcMod/CaseSplit.hs | 2 +- Language/Haskell/GhcMod/FillSig.hs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 5d2c414..1db05d7 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -66,7 +66,7 @@ splits file lineNo colNo = return (fourInts bndLoc, t) where handler (SomeException ex) = do - gmLog GmDebug "splits" $ + gmLog GmException "splits" $ text "" $$ nest 4 (showDoc ex) emptyResult =<< options diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index 02da7a0..ed1a769 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -361,7 +361,7 @@ refine file lineNo colNo (Expression expr) = in (fourInts loc, doParen paren txt) where handler (SomeException ex) = do - gmLog GmDebug "refining" $ + gmLog GmException "refining" $ text "" $$ nest 4 (showDoc ex) emptyResult =<< options @@ -450,7 +450,7 @@ auto file lineNo colNo = , map (doParen paren) $ nub (djinnsEmpty ++ djinns)) where handler (SomeException ex) = do - gmLog GmDebug "auto-refining" $ + gmLog GmException "auto-refining" $ text "" $$ nest 4 (showDoc ex) emptyResult =<< options From f1191a419e01278a4195e7cb2fc58670f4391564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 17 Aug 2015 07:03:32 +0200 Subject: [PATCH 040/147] Fix some warnings --- test/GhcPkgSpec.hs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/GhcPkgSpec.hs b/test/GhcPkgSpec.hs index 6f93404..768f2e4 100644 --- a/test/GhcPkgSpec.hs +++ b/test/GhcPkgSpec.hs @@ -1,20 +1,12 @@ module GhcPkgSpec where -import Control.Arrow -import Control.Applicative -import Distribution.Helper import Language.Haskell.GhcMod.GhcPkg -import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.CabalHelper -import Language.Haskell.GhcMod.Error import Test.Hspec -import System.Directory -import System.FilePath -import System.Process (readProcess, system) +import System.Process (system) import Dir import TestUtils -import Data.List spec :: Spec spec = do From 0d78ee4096df893de1b6caff684dca4aaed5d2ef Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Mon, 17 Aug 2015 08:43:34 +0300 Subject: [PATCH 041/147] getCanonicalFileNameSafe is now best-effort canonicalizatoin Canonicalizes longest init of path possible, and appends rest verbatim --- Language/Haskell/GhcMod/Utils.hs | 14 ++++++++------ src/GHCMod.hs | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index b675c06..377a7e7 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -27,6 +27,9 @@ import Control.Applicative import Data.Char import qualified Data.Map as M import Data.Maybe (fromMaybe) +import Data.Either (rights) +import Data.List (inits) +import System.FilePath (joinPath, splitPath) import Exception import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Types @@ -170,12 +173,11 @@ withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath getCanonicalFileNameSafe fn = do - crdl <- cradle - let ccfn = cradleRootDir crdl fn - fex <- liftIO $ doesFileExist ccfn - if fex - then liftIO $ canonicalizePath ccfn - else return ccfn + pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath fn) + return $ + if (length pl > 0) + then joinPath $ (head pl):(drop (length pl - 1) (splitPath fn)) + else error "Current dir doesn't seem to exist?" mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath) mkRevRedirMapFunc = do diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 44a6e30..eb57ed1 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -277,20 +277,20 @@ File map docs: CLI options: * `--map-file "file1.hs=file2.hs"` can be used to tell ghc-mod that it should take source code for `file1.hs` from `file2.hs`. - `file1.hs` can be either full path, or path relative to project root. + `file1.hs` can be either full path, or path relative to current directory. `file2.hs` has to be either relative to current directory, or full path (preferred). * `--map-file "file.hs"` can be used to tell ghc-mod that it should take source code for `file.hs` from stdin. File end marker is `\EOT\n`, i.e. `\x04\x0A`. `file.hs` may or may not exist, and should be - either full path, or relative to project root. + either full path, or relative to current directory. Interactive commands: * `map-file file.hs` -- tells ghc-modi to read `file.hs` source from stdin. Works the same as second form of `--map-file` CLI option. * `unmap-file file.hs` -- unloads previously mapped file, so that it's no longer mapped. `file.hs` can be full path or relative to - project root, either will work. + current directory, either will work. Exposed functions: * `loadMappedFile :: FilePath -> FilePath -> GhcModT m ()` -- maps `FilePath`, From 61922e249fca527cb61aedb60b246486d5d40f63 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 17 Aug 2015 12:59:52 +0900 Subject: [PATCH 042/147] fixing a bug that the cursor stays in the error buffer. --- elisp/ghc-process.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 497ecde..94dc5d7 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -126,8 +126,8 @@ (with-selected-window cwin (goto-char (point-max)) (insert-buffer-substring tbuf 1 end) - (set-buffer-modified-p nil) - (redisplay))))) + (set-buffer-modified-p nil)) + (redisplay)))) (delete-region 1 end))))) (goto-char (point-max)) (forward-line -1) From 3e0c06f50a78e42297bf2f5f50d36efa19094d4b Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 17 Aug 2015 15:43:34 +0900 Subject: [PATCH 043/147] Highlighting even strings with spaces. (#547) --- elisp/ghc-check.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index 37d02fc..7f16d63 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -186,7 +186,8 @@ nil do not display errors/warnings. (forward-line (1- line)) (forward-char (1- coln)) (setq beg (point)) - (skip-chars-forward "^[:space:]" (line-end-position)) + (forward-sexp) + ;; (skip-chars-forward "^[:space:]" (line-end-position)) (setq end (point))))) (t (setq beg (point)) From 3dea19b2703e3cff9efde0948263592245e8f111 Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Mon, 17 Aug 2015 10:39:49 +0300 Subject: [PATCH 044/147] Fix getCanonicalFileNameSafe to work on all relevant ghc versions --- Language/Haskell/GhcMod/Utils.hs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 377a7e7..2ee4e4d 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -29,7 +29,7 @@ import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Either (rights) import Data.List (inits) -import System.FilePath (joinPath, splitPath) +import System.FilePath (joinPath, splitPath, normalise) import Exception import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Types @@ -173,11 +173,18 @@ withMappedFile file action = getCanonicalFileNameSafe file >>= lookupMMappedFile getCanonicalFileNameSafe :: (IOish m, GmEnv m) => FilePath -> m FilePath getCanonicalFileNameSafe fn = do - pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath fn) + let fn' = normalise fn + pl <- liftIO $ rights <$> (mapM ((try :: IO FilePath -> IO (Either SomeException FilePath)) . canonicalizePath . joinPath) $ reverse $ inits $ splitPath' fn') return $ if (length pl > 0) - then joinPath $ (head pl):(drop (length pl - 1) (splitPath fn)) + then joinPath $ (head pl):(drop (length pl - 1) (splitPath fn')) else error "Current dir doesn't seem to exist?" + where +#if __GLASGOW_HASKELL__ < 710 + splitPath' = (".":) . splitPath +#else + splitPath' = splitPath +#endif mkRevRedirMapFunc :: (Functor m, GmState m, GmEnv m) => m (FilePath -> FilePath) mkRevRedirMapFunc = do From 45f8194c0c6b0cb0c874668c7c73bdfaac3d0b66 Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Sat, 11 Jul 2015 12:47:03 +0200 Subject: [PATCH 045/147] Set -fdefer-types-holes in case split --- Language/Haskell/GhcMod/CaseSplit.hs | 2 +- Language/Haskell/GhcMod/DynFlags.hs | 4 ++++ Language/Haskell/GhcMod/Gap.hs | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 1db05d7..4bf9588 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -47,7 +47,7 @@ splits :: IOish m -> Int -- ^ Column number. -> GhcModT m String splits file lineNo colNo = - ghandle handler $ runGmlT' [Left file] deferErrors $ do + ghandle handler $ runGmlT' [Left file] deferErrorsAndHoles $ do opt <- options crdl <- cradle style <- getStyle diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index f1950f7..aeea699 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -100,3 +100,7 @@ setNoMaxRelevantBindings = id deferErrors :: DynFlags -> Ghc DynFlags deferErrors df = return $ Gap.setWarnTypedHoles $ Gap.setDeferTypeErrors $ setNoWarningFlags df + +deferErrorsAndHoles :: DynFlags -> Ghc DynFlags +deferErrorsAndHoles df = return $ + Gap.setDeferTypeErrors $ Gap.setDeferTypedHoles $ setNoWarningFlags df diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 007976c..511f0bc 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -14,6 +14,7 @@ module Language.Haskell.GhcMod.Gap ( , setCabalPkg , setHideAllPackages , setDeferTypeErrors + , setDeferTypedHoles , setWarnTypedHoles , setDumpSplices , isDumpSplices @@ -294,6 +295,13 @@ setDeferTypeErrors dflag = dopt_set dflag Opt_DeferTypeErrors setDeferTypeErrors = id #endif +setDeferTypedHoles :: DynFlags -> DynFlags +#if __GLASGOW_HASKELL__ >= 708 +setDeferTypedHoles dflag = gopt_set dflag Opt_DeferTypedHoles +#else +setDeferTypedHoles = id +#endif + setWarnTypedHoles :: DynFlags -> DynFlags #if __GLASGOW_HASKELL__ >= 708 setWarnTypedHoles dflag = wopt_set dflag Opt_WarnTypedHoles From 78c5cea161d9667fade88eb9f29003f5d80b9f54 Mon Sep 17 00:00:00 2001 From: Anthony Cowley Date: Fri, 14 Aug 2015 12:18:26 -0400 Subject: [PATCH 046/147] DeferErrors implies DeferTypedHoles in GHC >= 7.10 The Opt_DeferErrors flag should imply Opt_DeferTypedHoles. The proper API for setting these flags that implements such implications is unfortunately not exposed by GHC. --- Language/Haskell/GhcMod/CaseSplit.hs | 2 +- Language/Haskell/GhcMod/DynFlags.hs | 7 ++----- Language/Haskell/GhcMod/Gap.hs | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 4bf9588..1db05d7 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -47,7 +47,7 @@ splits :: IOish m -> Int -- ^ Column number. -> GhcModT m String splits file lineNo colNo = - ghandle handler $ runGmlT' [Left file] deferErrorsAndHoles $ do + ghandle handler $ runGmlT' [Left file] deferErrors $ do opt <- options crdl <- cradle style <- getStyle diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index aeea699..796dc77 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -99,8 +99,5 @@ setNoMaxRelevantBindings = id deferErrors :: DynFlags -> Ghc DynFlags deferErrors df = return $ - Gap.setWarnTypedHoles $ Gap.setDeferTypeErrors $ setNoWarningFlags df - -deferErrorsAndHoles :: DynFlags -> Ghc DynFlags -deferErrorsAndHoles df = return $ - Gap.setDeferTypeErrors $ Gap.setDeferTypedHoles $ setNoWarningFlags df + Gap.setWarnTypedHoles $ Gap.setDeferTypedHoles $ + Gap.setDeferTypeErrors $ setNoWarningFlags df diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 511f0bc..41a10ae 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -296,7 +296,7 @@ setDeferTypeErrors = id #endif setDeferTypedHoles :: DynFlags -> DynFlags -#if __GLASGOW_HASKELL__ >= 708 +#if __GLASGOW_HASKELL__ >= 710 setDeferTypedHoles dflag = gopt_set dflag Opt_DeferTypedHoles #else setDeferTypedHoles = id From b4bb930037f746397a5ece06cf86da94d8359822 Mon Sep 17 00:00:00 2001 From: Anthony Cowley Date: Mon, 17 Aug 2015 15:36:21 -0400 Subject: [PATCH 047/147] Improved documentation browsing on OS X. Opening a tab in Safari using AppleScript preserves URL anchors. The use of this function may be toggled with a new customization option. --- elisp/ghc-doc.el | 27 +++++++++++++++++++++++---- elisp/ghc.el | 2 ++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/elisp/ghc-doc.el b/elisp/ghc-doc.el index 9c61125..512fa7d 100644 --- a/elisp/ghc-doc.el +++ b/elisp/ghc-doc.el @@ -10,6 +10,17 @@ (require 'ghc-comp) (require 'ghc-info) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Customize Variables +;;; + +(defcustom ghc-doc-browser-function #'browse-url + "Function used to browse documentation." + :type '(radio (function-item browse-url) + (function-item ghc-browse-url-safari)) + :group 'ghc-mod) + ;;; Code: (defun ghc-browse-document (&optional haskell-org) @@ -43,9 +54,18 @@ (defconst ghc-doc-hackage-format "http://hackage.haskell.org/packages/archive/%s/%s/doc/html/%s.html") +(defun ghc-browse-url-safari (uri &rest args) +"Open a URI in Safari using AppleScript. This preserves anchors." + (let ((script (format " +tell application \"Safari\" + open location \"%s\" + activate +end tell" uri))) + (do-applescript script))) + (defun ghc-display-document (pkg-ver-path mod haskell-org &optional symbol) - (let* ((mod- (ghc-replace-character mod ?. ?-)) - (pkg (ghc-pkg-ver-path-get-pkg pkg-ver-path)) + (let* ((pkg (ghc-pkg-ver-path-get-pkg pkg-ver-path)) + (mod- (ghc-replace-character mod ?. ?-)) (ver (ghc-pkg-ver-path-get-ver pkg-ver-path)) (path (ghc-pkg-ver-path-get-path pkg-ver-path)) (pkg-with-ver (format "%s-%s" pkg ver)) @@ -54,8 +74,7 @@ (file (format "%s/%s.html" path mod-)) (url0 (if (or haskell-org (not (file-exists-p file))) remote local)) (url (if symbol (ghc-add-anchor url0 symbol) url0))) - ;; Mac's "open" removes the anchor from "file://", sigh. - (browse-url url))) + (funcall ghc-doc-browser-function url))) (defun ghc-add-anchor (url symbol) (let ((case-fold-search nil)) diff --git a/elisp/ghc.el b/elisp/ghc.el index a555c47..bdad76c 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -30,6 +30,8 @@ (defconst ghc-version "5.3.0.0") +(defgroup ghc-mod '() "ghc-mod customization") + ;; (eval-when-compile ;; (require 'haskell-mode)) From a383f4693915f53cd847f0586b1a0fbb4d82b396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 04:27:02 +0200 Subject: [PATCH 048/147] Fix nasty module graph caching issue resolvedComponentsCache did not consider outgoing edges in the module graph when computing the set of dependent files i.e. for `A -> B, A -> C` `flatten` would give [B,C] instead of [A,B,C] --- Language/Haskell/GhcMod/Caching/Types.hs | 2 +- Language/Haskell/GhcMod/Target.hs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Caching/Types.hs b/Language/Haskell/GhcMod/Caching/Types.hs index ae32a7c..c4fce4c 100644 --- a/Language/Haskell/GhcMod/Caching/Types.hs +++ b/Language/Haskell/GhcMod/Caching/Types.hs @@ -47,6 +47,6 @@ data TimedCacheFiles = TimedCacheFiles { -- ^ 'cacheFile' timestamp tcFiles :: [TimedFile] -- ^ Timestamped files returned by the cached action - } + } deriving (Eq, Ord, Show) type ChCacheData = (Programs, FilePath, FilePath, (Version, [Char])) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index ffdcd1f..5391f58 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -267,7 +267,8 @@ resolvedComponentsCache = Cached { -> [FilePath] flatten = Map.elems >>> map (gmcHomeModuleGraph >>> gmgGraph - >>> Map.elems + >>> (Map.keysSet &&& Map.elems) + >>> uncurry insert >>> map (Set.map mpPath) >>> Set.unions ) From dd82cea99613bbdf5960287e0ed3a07fbc886bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 04:34:39 +0200 Subject: [PATCH 049/147] Demote no-component-assignment warning to debug, Fix #552 --- Language/Haskell/GhcMod/Target.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 5391f58..e444c15 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -211,7 +211,7 @@ targetGhcOptions crdl sefnmn = do -- First component should be ChLibName, if no lib will take lexically first exe. let cns = filter (/= ChSetupHsName) $ Map.keys mcs - gmLog GmWarning "" $ strDoc $ "Could not find a component assignment, falling back to picking library component in cabal file." + gmLog GmDebug "" $ strDoc $ "Could not find a component assignment, falling back to picking library component in cabal file." return $ gmcGhcOpts $ fromJust $ Map.lookup (head cns) mcs else do when noCandidates $ From cdc74e059c0f1bc5a002aef043c9c202f3ac1274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 04:50:19 +0200 Subject: [PATCH 050/147] Fix ghc-modi not exiting cleanly --- src/GHCMod.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index c3239ad..8bb8934 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -23,9 +23,8 @@ import System.FilePath (()) import System.Directory (setCurrentDirectory, getAppUserDataDirectory, removeDirectoryRecursive) import System.Environment (getArgs) -import System.Exit (exitFailure) import System.IO (stdout, hSetEncoding, utf8, hFlush) -import System.Exit (exitSuccess) +import System.Exit import Text.PrettyPrint import Prelude @@ -373,6 +372,8 @@ data InteractiveOptions = InteractiveOptions { handler :: IOish m => GhcModT m a -> GhcModT m a handler = flip gcatches $ [ GHandler $ \(FatalError msg) -> exitError msg + , GHandler $ \e@(ExitSuccess) -> throw e + , GHandler $ \e@(ExitFailure _) -> throw e , GHandler $ \(InvalidCommandLine e) -> do case e of Left cmd -> @@ -494,6 +495,8 @@ legacyInteractiveLoop symdbreq world = do where interactiveHandlers = [ GHandler $ \e@(FatalError _) -> throw e + , GHandler $ \e@(ExitSuccess) -> throw e + , GHandler $ \e@(ExitFailure _) -> throw e , GHandler $ \(SomeException e) -> gmErrStrLn (show e) >> return "" ] From f7149ba23fd197c0ad096574641ba10983302128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 04:50:51 +0200 Subject: [PATCH 051/147] Cleanup --- Language/Haskell/GhcMod/Target.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index e444c15..150fbbd 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -318,10 +318,11 @@ sandboxOpts crdl = do where (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) - getSandboxPackageDbStack :: FilePath - -- ^ Project Directory (where the cabal.sandbox.config - -- file would be if it exists) - -> IO [GhcPkgDb] + getSandboxPackageDbStack + :: FilePath + -- ^ Project Directory (where the cabal.sandbox.config file would be if + -- it exists) + -> IO [GhcPkgDb] getSandboxPackageDbStack cdir = ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb cdir From 90b1e452e26ca13df130b0ed4393b62f3671276d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 04:54:10 +0200 Subject: [PATCH 052/147] Some pretty printing "improvements" (hopefully) --- Language/Haskell/GhcMod/Pretty.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Pretty.hs b/Language/Haskell/GhcMod/Pretty.hs index 5526772..1df6948 100644 --- a/Language/Haskell/GhcMod/Pretty.hs +++ b/Language/Haskell/GhcMod/Pretty.hs @@ -55,11 +55,16 @@ fnDoc :: FilePath -> Doc fnDoc = doubleQuotes . text showDoc :: Show a => a -> Doc -showDoc = text . show +showDoc = strLnDoc . show warnDoc :: Doc -> Doc warnDoc d = text "Warning" <+>: d +strLnDoc :: String -> Doc +strLnDoc str = doc (dropWhileEnd isSpace str) + where + doc = lines >>> map text >>> foldr ($+$) empty + strDoc :: String -> Doc strDoc str = doc (dropWhileEnd isSpace str) where From bb3a948912bd91f41cbd528533ff3a0aa2dc9050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 18 Aug 2015 07:41:08 +0200 Subject: [PATCH 053/147] Fix over-qualified error messages (Fix #551) --- Language/Haskell/GhcMod/LightGhc.hs | 44 +++++++++++++++ Language/Haskell/GhcMod/Logger.hs | 84 ++++++++++++++--------------- Language/Haskell/GhcMod/Target.hs | 33 +----------- ghc-mod.cabal | 1 + test/CheckSpec.hs | 5 ++ test/HomeModuleGraphSpec.hs | 2 +- test/TargetSpec.hs | 1 + 7 files changed, 95 insertions(+), 75 deletions(-) create mode 100644 Language/Haskell/GhcMod/LightGhc.hs diff --git a/Language/Haskell/GhcMod/LightGhc.hs b/Language/Haskell/GhcMod/LightGhc.hs new file mode 100644 index 0000000..18aac05 --- /dev/null +++ b/Language/Haskell/GhcMod/LightGhc.hs @@ -0,0 +1,44 @@ +module Language.Haskell.GhcMod.LightGhc where + +import Control.Monad.Reader (runReaderT) +import Data.IORef + +import GHC +import GHC.Paths (libdir) +import StaticFlags +import SysTools +import DynFlags +import HscMain +import HscTypes + +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types +import Language.Haskell.GhcMod.DynFlags + +withLightHscEnv :: forall m a. IOish m + => [GHCOption] -> (HscEnv -> m a) -> m a +withLightHscEnv opts action = gbracket initEnv teardownEnv action + where + teardownEnv :: HscEnv -> m () + teardownEnv env = liftIO $ do + let dflags = hsc_dflags env + cleanTempFiles dflags + cleanTempDirs dflags + + initEnv :: m HscEnv + initEnv = liftIO $ do + initStaticOpts + settings <- initSysTools (Just libdir) + dflags <- initDynFlags (defaultDynFlags settings) + env <- newHscEnv dflags + dflags' <- runLightGhc env $ do + -- HomeModuleGraph and probably all other clients get into all sorts of + -- trouble if the package state isn't initialized here + _ <- setSessionDynFlags =<< addCmdOpts opts =<< getSessionDynFlags + getSessionDynFlags + newHscEnv dflags' + +runLightGhc :: HscEnv -> LightGhc a -> IO a +runLightGhc env action = do + renv <- newIORef env + flip runReaderT renv $ unLightGhc action diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 5423c52..fc3ca6e 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -8,15 +8,17 @@ module Language.Haskell.GhcMod.Logger ( import Control.Arrow import Control.Applicative -import Data.List (isPrefixOf) -import Data.Maybe (fromMaybe) +import Data.Ord +import Data.List +import Data.Maybe +import Data.Function import Control.Monad.Reader (Reader, asks, runReader) import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) import System.FilePath (normalise) import Text.PrettyPrint -import ErrUtils (ErrMsg, errMsgShortDoc, errMsgExtraInfo) -import GHC (DynFlags, SrcSpan, Severity(SevError)) +import ErrUtils +import GHC import HscTypes import Outputable import qualified GHC as G @@ -38,7 +40,6 @@ data Log = Log [String] Builder newtype LogRef = LogRef (IORef Log) data GmPprEnv = GmPprEnv { gpeDynFlags :: DynFlags - , gpePprStyle :: PprStyle , gpeMapFile :: FilePath -> FilePath } @@ -56,18 +57,23 @@ readAndClearLogRef (LogRef ref) = do writeIORef ref emptyLog return $ b [] -appendLogRef :: GmPprEnv -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () -appendLogRef rs df (LogRef ref) _ sev src st msg = modifyIORef ref update +appendLogRef :: (FilePath -> FilePath) -> DynFlags -> LogRef -> DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO () +appendLogRef rfm df (LogRef ref) _ sev src st msg = do + modifyIORef ref update where - l = runReader (ppMsg src sev msg) rs{gpeDynFlags=df, gpePprStyle=st} + gpe = GmPprEnv { + gpeDynFlags = df + , gpeMapFile = rfm + } + l = runReader (ppMsg st src sev msg) gpe + update lg@(Log ls b) | l `elem` ls = lg | otherwise = Log (l:ls) (b . (l:)) ---------------------------------------------------------------- --- | Set the session flag (e.g. "-Wall" or "-w:") then --- executes a body. Logged messages are returned as 'String'. +-- | Logged messages are returned as 'String'. -- Right is success and Left is failure. withLogger :: (GmGhc m, GmEnv m, GmState m) => (DynFlags -> DynFlags) @@ -88,73 +94,67 @@ withLogger' env action = do rfm <- mkRevRedirMapFunc - let dflags = hsc_dflags env - pu = icPrintUnqual dflags (hsc_IC env) - stl = mkUserStyle pu AllTheWay - st = GmPprEnv { - gpeDynFlags = dflags - , gpePprStyle = stl + let setLogger df = Gap.setLogAction df $ appendLogRef rfm df logref + handlers = [ + GHandler $ \ex -> return $ Left $ runReader (sourceError ex) gpe, + GHandler $ \ex -> return $ Left [render $ ghcExceptionDoc ex] + ] + gpe = GmPprEnv { + gpeDynFlags = hsc_dflags env , gpeMapFile = rfm } - setLogger df = Gap.setLogAction df $ appendLogRef st df logref - handlers = [ - GHandler $ \ex -> return $ Left $ runReader (sourceError ex) st, - GHandler $ \ex -> return $ Left [render $ ghcExceptionDoc ex] - ] - a <- gcatches (Right <$> action setLogger) handlers ls <- liftIO $ readAndClearLogRef logref return ((,) ls <$> a) -errBagToStrList :: (Functor m, GmState m, GmEnv m) => HscEnv -> Bag ErrMsg -> m [String] -errBagToStrList env errs = let - dflags = hsc_dflags env - pu = icPrintUnqual dflags (hsc_IC env) - st = mkUserStyle pu AllTheWay - in do +errBagToStrList :: (IOish m, GmState m, GmEnv m) => HscEnv -> Bag ErrMsg -> m [String] +errBagToStrList env errs = do rfm <- mkRevRedirMapFunc return $ runReader - (errsToStr (bagToList errs)) - GmPprEnv{gpeDynFlags=dflags, gpePprStyle=st, gpeMapFile=rfm} + (errsToStr (sortMsgBag errs)) + GmPprEnv{ gpeDynFlags = hsc_dflags env, gpeMapFile = rfm } ---------------------------------------------------------------- -- | Converting 'SourceError' to 'String'. sourceError :: SourceError -> GmPprEnvM [String] -sourceError = errsToStr . reverse . bagToList . srcErrorMessages +sourceError = errsToStr . sortMsgBag . srcErrorMessages errsToStr :: [ErrMsg] -> GmPprEnvM [String] errsToStr = mapM ppErrMsg +sortMsgBag :: Bag ErrMsg -> [ErrMsg] +sortMsgBag bag = sortBy (compare `on` Gap.errorMsgSpan) $ bagToList bag + ---------------------------------------------------------------- ppErrMsg :: ErrMsg -> GmPprEnvM String ppErrMsg err = do - dflag <- asks gpeDynFlags - st <- asks gpePprStyle - let ext = showPage dflag st (errMsgExtraInfo err) - m <- ppMsg spn SevError msg + dflags <- asks gpeDynFlags + let unqual = errMsgContext err + st = mkErrStyle dflags unqual + let ext = showPage dflags st (errMsgExtraInfo err) + m <- ppMsg st spn SevError msg return $ m ++ (if null ext then "" else "\n" ++ ext) where spn = Gap.errorMsgSpan err msg = errMsgShortDoc err -ppMsg :: SrcSpan -> Severity-> SDoc -> GmPprEnvM String -ppMsg spn sev msg = do - dflag <- asks gpeDynFlags - st <- asks gpePprStyle - let cts = showPage dflag st msg +ppMsg :: PprStyle -> SrcSpan -> Severity -> SDoc -> GmPprEnvM String +ppMsg st spn sev msg = do + dflags <- asks gpeDynFlags + let cts = showPage dflags st msg prefix <- ppMsgPrefix spn sev cts return $ prefix ++ cts ppMsgPrefix :: SrcSpan -> Severity -> String -> GmPprEnvM String ppMsgPrefix spn sev cts = do - dflag <- asks gpeDynFlags + dflags <- asks gpeDynFlags mr <- asks gpeMapFile let defaultPrefix - | Gap.isDumpSplices dflag = "" + | Gap.isDumpSplices dflags = "" | otherwise = checkErrorPrefix return $ fromMaybe defaultPrefix $ do (line,col,_,_) <- Gap.getSrcSpan spn diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 150fbbd..de73b13 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -20,14 +20,10 @@ module Language.Haskell.GhcMod.Target where import Control.Arrow import Control.Applicative import Control.Category ((.)) -import Control.Monad.Reader (runReaderT) import GHC import GHC.Paths (libdir) -import StaticFlags import SysTools import DynFlags -import HscMain -import HscTypes import Language.Haskell.GhcMod.DynFlags import Language.Haskell.GhcMod.Monad.Types @@ -40,6 +36,7 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils as U import Language.Haskell.GhcMod.FileMapping +import Language.Haskell.GhcMod.LightGhc import Data.Maybe import Data.Monoid as Monoid @@ -60,34 +57,6 @@ import Prelude hiding ((.)) import System.Directory import System.FilePath -withLightHscEnv :: forall m a. IOish m - => [GHCOption] -> (HscEnv -> m a) -> m a -withLightHscEnv opts action = gbracket initEnv teardownEnv action - where - teardownEnv :: HscEnv -> m () - teardownEnv env = liftIO $ do - let dflags = hsc_dflags env - cleanTempFiles dflags - cleanTempDirs dflags - - initEnv :: m HscEnv - initEnv = liftIO $ do - initStaticOpts - settings <- initSysTools (Just libdir) - dflags <- initDynFlags (defaultDynFlags settings) - env <- newHscEnv dflags - dflags' <- runLightGhc env $ do - -- HomeModuleGraph and probably all other clients get into all sorts of - -- trouble if the package state isn't initialized here - _ <- setSessionDynFlags =<< addCmdOpts opts =<< getSessionDynFlags - getSessionDynFlags - newHscEnv dflags' - -runLightGhc :: HscEnv -> LightGhc a -> IO a -runLightGhc env action = do - renv <- newIORef env - flip runReaderT renv $ unLightGhc action - runGmPkgGhc :: (IOish m, GmEnv m, GmState m, GmLog m) => LightGhc a -> m a runGmPkgGhc action = do pkgOpts <- packageGhcOptions diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 105a1f5..bb579b7 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -118,6 +118,7 @@ Library Language.Haskell.GhcMod.Info Language.Haskell.GhcMod.Lang Language.Haskell.GhcMod.Lint + Language.Haskell.GhcMod.LightGhc Language.Haskell.GhcMod.Logger Language.Haskell.GhcMod.Logging Language.Haskell.GhcMod.Modules diff --git a/test/CheckSpec.hs b/test/CheckSpec.hs index cc9b219..251de2b 100644 --- a/test/CheckSpec.hs +++ b/test/CheckSpec.hs @@ -67,3 +67,8 @@ spec = do _ <- system "cabal build" res <- runD $ checkSyntax ["Main.hs"] res `shouldBe` "Preprocessed.hsc:3:1:Warning: Top-level binding with no type signature: warning :: ()\n" + + it "Uses the right qualification style" $ do + withDirectory_ "test/data/nice-qualification" $ do + res <- runD $ checkSyntax ["NiceQualification.hs"] + res `shouldBe` "NiceQualification.hs:4:8:Couldn't match expected type \8216IO ()\8217 with actual type \8216[Char]\8217\NULIn the expression: \"wrong type\"\NULIn an equation for \8216main\8217: main = \"wrong type\"\n" diff --git a/test/HomeModuleGraphSpec.hs b/test/HomeModuleGraphSpec.hs index 7e43140..e8918cc 100644 --- a/test/HomeModuleGraphSpec.hs +++ b/test/HomeModuleGraphSpec.hs @@ -19,7 +19,7 @@ module HomeModuleGraphSpec where import Language.Haskell.GhcMod.HomeModuleGraph -import Language.Haskell.GhcMod.Target +import Language.Haskell.GhcMod.LightGhc import TestUtils import GHC diff --git a/test/TargetSpec.hs b/test/TargetSpec.hs index 9207b65..fda45a2 100644 --- a/test/TargetSpec.hs +++ b/test/TargetSpec.hs @@ -2,6 +2,7 @@ module TargetSpec where import Language.Haskell.GhcMod.Target +import Language.Haskell.GhcMod.LightGhc import Language.Haskell.GhcMod.Gap import Test.Hspec From 7ac5aca5a5933ed81e656a208ee31be812d701c6 Mon Sep 17 00:00:00 2001 From: Martijn Schrage Date: Tue, 18 Aug 2015 17:13:54 +0200 Subject: [PATCH 054/147] Use https in Location url to get clickable link on hackage web page --- ghc-mod.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 105a1f5..e1876f1 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -251,4 +251,4 @@ Test-Suite spec Source-Repository head Type: git - Location: git://github.com/kazu-yamamoto/ghc-mod.git + Location: https://github.com/kazu-yamamoto/ghc-mod.git From e198f68f2a39780ce66447b996479d414df946c9 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Tue, 18 Aug 2015 10:54:55 +0200 Subject: [PATCH 055/147] Rearrange loadTargets code. The loadTargets function is exposed via the Internal module for use by external programmes, such as HaRe. Re-arrange to code so that it can still be called with a list of string targets, as it was before. --- Language/Haskell/GhcMod/Internal.hs | 1 + Language/Haskell/GhcMod/Target.hs | 31 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Language/Haskell/GhcMod/Internal.hs b/Language/Haskell/GhcMod/Internal.hs index d5fdff7..76eb7fb 100644 --- a/Language/Haskell/GhcMod/Internal.hs +++ b/Language/Haskell/GhcMod/Internal.hs @@ -40,6 +40,7 @@ module Language.Haskell.GhcMod.Internal ( , cradle , getCompilerMode , setCompilerMode + , targetGhcOptions , withOptions -- * 'GhcModError' , gmeDoc diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index de73b13..3429818 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -137,20 +137,8 @@ runGmlTWith efnmns' mdf wrapper action = do let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns unGmlT $ wrapper $ do - targets <- - withLightHscEnv opts $ \env -> - liftM (nubBy ((==) `on` targetId)) - (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs) - >>= mapM relativize - loadTargets targets + loadTargets opts targetStrs action - where - relativize (Target (TargetFile filePath phase) taoc src) = do - crdl <- cradle - let tid = TargetFile relativeFilePath phase - relativeFilePath = makeRelative (cradleRootDir crdl) filePath - return $ Target tid taoc src - relativize tgt = return tgt targetGhcOptions :: forall m. IOish m => Cradle @@ -413,8 +401,14 @@ resolveGmComponents mumns cs = do same f a b = (f a) == (f b) -- | Set the files as targets and load them. -loadTargets :: IOish m => [Target] -> GmlT m () -loadTargets targets = do +loadTargets :: IOish m => [GHCOption] -> [FilePath] -> GmlT m () +loadTargets opts targetStrs = do + targets <- + withLightHscEnv opts $ \env -> + liftM (nubBy ((==) `on` targetId)) + (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs) + >>= mapM relativize + gmLog GmDebug "loadTargets" $ text "Loading" <+>: fsep (map (text . showTargetId) targets) @@ -435,6 +429,13 @@ loadTargets targets = do else loadTargets' Simple where + relativize (Target (TargetFile filePath phase) taoc src) = do + crdl <- cradle + let tid = TargetFile relativeFilePath phase + relativeFilePath = makeRelative (cradleRootDir crdl) filePath + return $ Target tid taoc src + relativize tgt = return tgt + loadTargets' Simple = do void $ load LoadAllTargets mapM_ (parseModule >=> typecheckModule >=> desugarModule) =<< getModuleGraph From 2bd12ee41701bab748e6d20b16cc07ab4848607e Mon Sep 17 00:00:00 2001 From: Nikolay Yakimov Date: Wed, 19 Aug 2015 05:28:26 +0300 Subject: [PATCH 056/147] Fix --map-file docs --- src/GHCMod.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index eb57ed1..2a65323 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -277,20 +277,20 @@ File map docs: CLI options: * `--map-file "file1.hs=file2.hs"` can be used to tell ghc-mod that it should take source code for `file1.hs` from `file2.hs`. - `file1.hs` can be either full path, or path relative to current directory. - `file2.hs` has to be either relative to current directory, + `file1.hs` can be either full path, or path relative to project root. + `file2.hs` has to be either relative to project root, or full path (preferred). * `--map-file "file.hs"` can be used to tell ghc-mod that it should take source code for `file.hs` from stdin. File end marker is `\EOT\n`, i.e. `\x04\x0A`. `file.hs` may or may not exist, and should be - either full path, or relative to current directory. + either full path, or relative to project root. Interactive commands: * `map-file file.hs` -- tells ghc-modi to read `file.hs` source from stdin. Works the same as second form of `--map-file` CLI option. * `unmap-file file.hs` -- unloads previously mapped file, so that it's no longer mapped. `file.hs` can be full path or relative to - current directory, either will work. + project root, either will work. Exposed functions: * `loadMappedFile :: FilePath -> FilePath -> GhcModT m ()` -- maps `FilePath`, From 50ab2091d6fe226417e8999cae24062506fb497e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 19 Aug 2015 06:58:38 +0200 Subject: [PATCH 057/147] Add missing test files --- ghc-mod.cabal | 1 + test/data/nice-qualification/NiceQualification.hs | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 test/data/nice-qualification/NiceQualification.hs diff --git a/ghc-mod.cabal b/ghc-mod.cabal index bb579b7..e77cea3 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -84,6 +84,7 @@ Extra-Source-Files: ChangeLog test/data/file-mapping/*.hs test/data/file-mapping/preprocessor/*.hs test/data/file-mapping/lhs/*.lhs + test/data/nice-qualification/*.hs Library Default-Language: Haskell2010 diff --git a/test/data/nice-qualification/NiceQualification.hs b/test/data/nice-qualification/NiceQualification.hs new file mode 100644 index 0000000..bd416f4 --- /dev/null +++ b/test/data/nice-qualification/NiceQualification.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = "wrong type" From b7cea06a523d99d8b58f6e503d24f0f19c7eaa11 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:02:00 +0900 Subject: [PATCH 058/147] Flushing stdout to solve infinite loop (#542). --- Language/Haskell/GhcMod/Output.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index e96956a..6e3f36c 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -129,13 +129,14 @@ stdoutGateway chan = go ("", "") case ty of GmTerminated -> case stream of - GmOut -> putStr (obuf++l) >> go ("", ebuf) - GmErr -> putStr (ebuf++l) >> go (obuf, "") + GmOut -> putStr (obuf++l) >> hFlush stdout >> go ("", ebuf) + GmErr -> putStr (ebuf++l) >> hFlush stdout >> go (obuf, "") GmPartial -> case reverse $ lines l of [] -> go buf [x] -> go (appendBuf stream buf x) x:xs -> do putStr $ unlines $ reverse xs + hFlush stdout go (appendBuf stream buf x) appendBuf GmOut (obuf, ebuf) s = (obuf++s, ebuf) From c3dff6438962e25b4296dcf1bffa5c30bbd3fd5a Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:21:59 +0900 Subject: [PATCH 059/147] M-t now replace type hole. (#545). --- elisp/ghc-check.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index 7f16d63..5819a17 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -420,6 +420,10 @@ nil do not display errors/warnings. (let ((old (match-string 1 data)) (new (match-string 2 data))) (ghc-check-replace old new))) + ((string-match "Found hole .\\(_[_[:alnum:]]*\\). with type: \\([^\t\n]+\\)" data) + (let ((old (match-string 1 data)) + (new (match-string 2 data))) + (ghc-check-replace old new))) (t (setq ret nil))))))) From e360f7eb44e81c8dd37e377a1fa3de82ca51f7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 19 Aug 2015 08:11:29 +0200 Subject: [PATCH 060/147] Fix older GHCs --- Language/Haskell/GhcMod/Gap.hs | 8 ++++++++ Language/Haskell/GhcMod/Logger.hs | 2 +- test/CheckSpec.hs | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 41a10ae..198c3a4 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -42,6 +42,7 @@ module Language.Haskell.GhcMod.Gap ( , lookupModulePackageInAllPackages , Language.Haskell.GhcMod.Gap.isSynTyCon , parseModuleHeader + , mkErrStyle' ) where import Control.Applicative hiding (empty) @@ -559,3 +560,10 @@ parseModuleHeader str dflags filename = POk pst rdr_module -> let (warns,_) = getMessages pst in Right (warns, rdr_module) + +mkErrStyle' :: DynFlags -> PrintUnqualified -> PprStyle +#if __GLASGOW_HASKELL__ >= 706 +mkErrStyle' = Outputable.mkErrStyle +#else +mkErrStyle' _ = Outputable.mkErrStyle +#endif diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index fc3ca6e..2cb3247 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -134,7 +134,7 @@ ppErrMsg :: ErrMsg -> GmPprEnvM String ppErrMsg err = do dflags <- asks gpeDynFlags let unqual = errMsgContext err - st = mkErrStyle dflags unqual + st = Gap.mkErrStyle' dflags unqual let ext = showPage dflags st (errMsgExtraInfo err) m <- ppMsg st spn SevError msg return $ m ++ (if null ext then "" else "\n" ++ ext) diff --git a/test/CheckSpec.hs b/test/CheckSpec.hs index 251de2b..1ff26e2 100644 --- a/test/CheckSpec.hs +++ b/test/CheckSpec.hs @@ -71,4 +71,8 @@ spec = do it "Uses the right qualification style" $ do withDirectory_ "test/data/nice-qualification" $ do res <- runD $ checkSyntax ["NiceQualification.hs"] +#if __GLASGOW_HASKELL__ >= 708 res `shouldBe` "NiceQualification.hs:4:8:Couldn't match expected type \8216IO ()\8217 with actual type \8216[Char]\8217\NULIn the expression: \"wrong type\"\NULIn an equation for \8216main\8217: main = \"wrong type\"\n" +#else + res `shouldBe` "NiceQualification.hs:4:8:Couldn't match expected type `IO ()' with actual type `[Char]'\NULIn the expression: \"wrong type\"\NULIn an equation for `main': main = \"wrong type\"\n" +#endif From 320b404a8ce25f4646366eee396bc61e3a7eedae Mon Sep 17 00:00:00 2001 From: scturtle Date: Mon, 17 Aug 2015 13:41:46 +0800 Subject: [PATCH 061/147] Yet another try to support Stack. --- Language/Haskell/GhcMod/CabalHelper.hs | 12 ++++++++++++ Language/Haskell/GhcMod/Cradle.hs | 18 +++++++++++++++++- Language/Haskell/GhcMod/GhcPkg.hs | 2 ++ Language/Haskell/GhcMod/PathsAndFiles.hs | 3 +++ Language/Haskell/GhcMod/Target.hs | 10 ++++++++++ Language/Haskell/GhcMod/Types.hs | 2 +- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index ef6f501..f01a769 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -20,6 +20,7 @@ module Language.Haskell.GhcMod.CabalHelper ( getComponents , getGhcMergedPkgOptions , getCabalPackageDbStack + , getStackPackageDbStack , getCustomPkgDbStack , prepareCabalHelper ) @@ -43,6 +44,7 @@ import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Output import System.FilePath +import System.Directory (findExecutable) import Prelude hiding ((.)) import Paths_ghc_mod as GhcMod @@ -132,6 +134,16 @@ getCustomPkgDbStack = do mCusPkgDbFile <- liftIO . (traverse readFile <=< findCustomPackageDbFile) . cradleRootDir =<< cradle return $ parseCustomPackageDb <$> mCusPkgDbFile +getStackPackageDbStack :: IOish m => m [GhcPkgDb] +getStackPackageDbStack = do + mstack <- liftIO $ findExecutable "stack" + case mstack of + Nothing -> return [] + Just stack -> do + snapshotDb <- liftIO $ readProcess stack ["path", "--snapshot-pkg-db"] "" + localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] "" + return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb] + withCabal :: (IOish m, GmEnv m, GmLog m) => m a -> m a withCabal action = do crdl <- cradle diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 78e041d..19f17c9 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -29,7 +29,7 @@ findCradle = findCradle' =<< getCurrentDirectory findCradle' :: FilePath -> IO Cradle findCradle' dir = run $ do - (cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir) + (stackCradle dir `mplus` cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir) where run a = fillTempDir =<< (fromJust <$> runMaybeT a) findSpecCradle :: FilePath -> IO Cradle @@ -67,6 +67,22 @@ cabalCradle wdir = do , cradleCabalFile = Just cabalFile } +stackCradle :: FilePath -> MaybeT IO Cradle +stackCradle wdir = do + cabalFile <- MaybeT $ findCabalFile wdir + + let cabalDir = takeDirectory cabalFile + + stackConfigFile <- MaybeT $ findStackConfigFile cabalDir + + return Cradle { + cradleProjectType = StackProject + , cradleCurrentDir = wdir + , cradleRootDir = cabalDir + , cradleTempDir = error "tmpDir" + , cradleCabalFile = Just cabalFile + } + sandboxCradle :: FilePath -> MaybeT IO Cradle sandboxCradle wdir = do sbDir <- MaybeT $ findCabalSandboxDir wdir diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index f6c281b..86255fc 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -71,6 +71,8 @@ getPackageDbStack = do return $ [GlobalDb, db] CabalProject -> getCabalPackageDbStack + StackProject -> + getStackPackageDbStack return $ fromMaybe stack mCusPkgStack getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath] diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 085cf52..7f9249d 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -71,6 +71,9 @@ findCabalFile dir = do appendDir :: DirPath -> [FileName] -> [FilePath] appendDir d fs = (d ) `map` fs +findStackConfigFile :: FilePath -> IO (Maybe FilePath) +findStackConfigFile dir = mightExist (dir "stack.yaml") + -- | Get path to sandbox config file getSandboxDb :: FilePath -- ^ Path to the cabal package root directory (containing the diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 3429818..eb92204 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -149,6 +149,7 @@ targetGhcOptions crdl sefnmn = do case cradleProjectType crdl of CabalProject -> cabalOpts crdl + StackProject -> stackOpts crdl _ -> sandboxOpts crdl where zipMap f l = l `zip` (f `map` l) @@ -264,8 +265,17 @@ packageGhcOptions = do crdl <- cradle case cradleProjectType crdl of CabalProject -> getGhcMergedPkgOptions + StackProject -> stackOpts crdl _ -> sandboxOpts crdl +stackOpts :: MonadIO m => Cradle -> m [String] +stackOpts crdl = do + pkgDbStack <- liftIO getStackPackageDbStack + let pkgOpts = ghcDbStackOpts pkgDbStack + return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"] + where + (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) + -- also works for plain projects! sandboxOpts :: MonadIO m => Cradle -> m [String] sandboxOpts crdl = do diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index b632b2a..b3c99ea 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -121,7 +121,7 @@ defaultOptions = Options { ---------------------------------------------------------------- -data ProjectType = CabalProject | SandboxProject | PlainProject +data ProjectType = CabalProject | SandboxProject | PlainProject | StackProject deriving (Eq, Show) -- | The environment where this library is used. From 89c3faea05ca2fb3d8e109e7d149ad914927f73e Mon Sep 17 00:00:00 2001 From: scturtle Date: Mon, 17 Aug 2015 17:20:43 +0800 Subject: [PATCH 062/147] Initilize test for stack project --- Language/Haskell/GhcMod/Cradle.hs | 2 +- test/CabalHelperSpec.hs | 7 ++++ test/Main.hs | 5 +++ test/PathsAndFilesSpec.hs | 4 +++ test/data/stack-project/Setup.hs | 2 ++ test/data/stack-project/app/Main.hs | 6 ++++ test/data/stack-project/new-template.cabal | 41 ++++++++++++++++++++++ test/data/stack-project/src/Lib.hs | 6 ++++ test/data/stack-project/test/Spec.hs | 2 ++ 9 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 test/data/stack-project/Setup.hs create mode 100644 test/data/stack-project/app/Main.hs create mode 100644 test/data/stack-project/new-template.cabal create mode 100644 test/data/stack-project/src/Lib.hs create mode 100644 test/data/stack-project/test/Spec.hs diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 19f17c9..15c8981 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -73,7 +73,7 @@ stackCradle wdir = do let cabalDir = takeDirectory cabalFile - stackConfigFile <- MaybeT $ findStackConfigFile cabalDir + _stackConfigFile <- MaybeT $ findStackConfigFile cabalDir return Cradle { cradleProjectType = StackProject diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 42211d8..69e3814 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -56,6 +56,13 @@ spec = do then forM_ opts (\o -> o `shouldContain` ["-no-user-package-conf","-package-conf", cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) else forM_ opts (\o -> o `shouldContain` ["-no-user-package-db","-package-db",cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) + it "handles stack project" $ do + let tdir = "test/data/stack-project" + opts <- map gmcGhcOpts <$> runD' tdir getComponents + let ghcOpts = head opts + pkgs = pkgOptions ghcOpts + pkgs `shouldBe` ["Cabal","base","new-template"] + it "extracts build dependencies" $ do let tdir = "test/data/cabal-project" opts <- map gmcGhcOpts <$> runD' tdir getComponents diff --git a/test/Main.hs b/test/Main.hs index 18aa1eb..bbc6804 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -28,6 +28,11 @@ main = do genSandboxCfg `mapM_` sandboxes genGhcPkgCache `mapM_` pkgDirs + let stackDir = "test/data/stack-project" + withDirectory stackDir $ \_ -> do + system "stack init --force" + system "stack build" + let caches = [ "setup-config" , "setup-config.ghc-mod.cabal-helper" , "setup-config.ghc-mod.cabal-components" diff --git a/test/PathsAndFilesSpec.hs b/test/PathsAndFilesSpec.hs index 760b583..1e15b1d 100644 --- a/test/PathsAndFilesSpec.hs +++ b/test/PathsAndFilesSpec.hs @@ -25,6 +25,10 @@ spec = do it "finds cabal files in parent directories" $ do findCabalFile "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just "test/data/cabal-project/cabalapi.cabal" + describe "findStackConfigFile" $ do + it "works" $ do + findStackConfigFile "test/data/stack-project" `shouldReturn` Just "test/data/stack-project/stack.yaml" + describe "findCabalSandboxDir" $ do it "works" $ do findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just "test/data/cabal-project" diff --git a/test/data/stack-project/Setup.hs b/test/data/stack-project/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/test/data/stack-project/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/test/data/stack-project/app/Main.hs b/test/data/stack-project/app/Main.hs new file mode 100644 index 0000000..de1c1ab --- /dev/null +++ b/test/data/stack-project/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Lib + +main :: IO () +main = someFunc diff --git a/test/data/stack-project/new-template.cabal b/test/data/stack-project/new-template.cabal new file mode 100644 index 0000000..34a3a0b --- /dev/null +++ b/test/data/stack-project/new-template.cabal @@ -0,0 +1,41 @@ +name: new-template +version: 0.1.0.0 +synopsis: Initial project template from stack +description: Please see README.md +homepage: http://github.com/name/project +-- license: BSD3 +-- license-file: LICENSE +author: Your name here +maintainer: your.address@example.com +-- copyright: +category: Web +build-type: Simple +-- extra-source-files: +cabal-version: >=1.10 + +library + hs-source-dirs: src + exposed-modules: Lib + build-depends: base >= 4.7 && < 5 + default-language: Haskell2010 + +executable new-template-exe + hs-source-dirs: app + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: base + , new-template + default-language: Haskell2010 + +test-suite new-template-test + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Spec.hs + build-depends: base + , new-template + ghc-options: -threaded -rtsopts -with-rtsopts=-N + default-language: Haskell2010 + +source-repository head + type: git + location: https://github.com/name/project diff --git a/test/data/stack-project/src/Lib.hs b/test/data/stack-project/src/Lib.hs new file mode 100644 index 0000000..d36ff27 --- /dev/null +++ b/test/data/stack-project/src/Lib.hs @@ -0,0 +1,6 @@ +module Lib + ( someFunc + ) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/test/data/stack-project/test/Spec.hs b/test/data/stack-project/test/Spec.hs new file mode 100644 index 0000000..cd4753f --- /dev/null +++ b/test/data/stack-project/test/Spec.hs @@ -0,0 +1,2 @@ +main :: IO () +main = putStrLn "Test suite not yet implemented" From ba13688413195f3de70269fdaff74171aaf0208d Mon Sep 17 00:00:00 2001 From: scturtle Date: Tue, 18 Aug 2015 11:45:13 +0800 Subject: [PATCH 063/147] Tell travis to download stack binary file --- .travis.yml | 9 +++++++++ test/CabalHelperSpec.hs | 4 ++-- test/Main.hs | 7 ++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c134ceb..d8d96ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,15 @@ cache: before_cache: - rm -f $HOME/.cabal/logs $HOME/.cabal/packages/*/build-reports.log +before_install: + - wget https://github.com/commercialhaskell/stack/releases/download/v0.1.3.1/stack-0.1.3.1-x86_64-linux.gz + - mkdir stack-bin + - gunzip stack-0.1.3.1-x86_64-linux.gz + - mv stack-0.1.3.1-x86_64-linux stack-bin/stack + - chmod +x stack-bin/stack + - export PATH=$(pwd)/stack-bin:$PATH + - stack --version + install: - cabal update # - ( $CABAL122 && cabal install cabal-install --constraint "Cabal >= 1.22" && ghc-pkg unregister Cabal ) || true diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 69e3814..fa96172 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -56,12 +56,12 @@ spec = do then forM_ opts (\o -> o `shouldContain` ["-no-user-package-conf","-package-conf", cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) else forM_ opts (\o -> o `shouldContain` ["-no-user-package-db","-package-db",cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) - it "handles stack project" $ do + {- it "handles stack project" $ do let tdir = "test/data/stack-project" opts <- map gmcGhcOpts <$> runD' tdir getComponents let ghcOpts = head opts pkgs = pkgOptions ghcOpts - pkgs `shouldBe` ["Cabal","base","new-template"] + pkgs `shouldBe` ["Cabal","base","new-template"] -} it "extracts build dependencies" $ do let tdir = "test/data/cabal-project" diff --git a/test/Main.hs b/test/Main.hs index bbc6804..b9827d0 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -29,9 +29,10 @@ main = do genGhcPkgCache `mapM_` pkgDirs let stackDir = "test/data/stack-project" - withDirectory stackDir $ \_ -> do - system "stack init --force" - system "stack build" + void $ withDirectory_ stackDir $ do + void $ system "stack init --force" + void $ system "stack setup" + void $ system "stack build" let caches = [ "setup-config" , "setup-config.ghc-mod.cabal-helper" From cbb8feb0ad3a272043f7c5a23364de0127898e43 Mon Sep 17 00:00:00 2001 From: scturtle Date: Tue, 18 Aug 2015 15:33:18 +0800 Subject: [PATCH 064/147] Add test project in cabal file --- ghc-mod.cabal | 5 +++++ test/CabalHelperSpec.hs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index e77cea3..ab2d1ae 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -85,6 +85,11 @@ Extra-Source-Files: ChangeLog test/data/file-mapping/preprocessor/*.hs test/data/file-mapping/lhs/*.lhs test/data/nice-qualification/*.hs + test/data/stack-project/new-template.cabal + test/data/stack-project/Setup.hs + test/data/stack-project/app/Main.hs + test/data/stack-project/src/Lib.hs + test/data/stack-project/test/Spec.hs Library Default-Language: Haskell2010 diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index fa96172..69e3814 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -56,12 +56,12 @@ spec = do then forM_ opts (\o -> o `shouldContain` ["-no-user-package-conf","-package-conf", cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) else forM_ opts (\o -> o `shouldContain` ["-no-user-package-db","-package-db",cwd "test/data/cabal-project/.cabal-sandbox/"++ghcSandboxPkgDbDir bp]) - {- it "handles stack project" $ do + it "handles stack project" $ do let tdir = "test/data/stack-project" opts <- map gmcGhcOpts <$> runD' tdir getComponents let ghcOpts = head opts pkgs = pkgOptions ghcOpts - pkgs `shouldBe` ["Cabal","base","new-template"] -} + pkgs `shouldBe` ["Cabal","base","new-template"] it "extracts build dependencies" $ do let tdir = "test/data/cabal-project" From a285b4220606971570da64a48348a684f0cf8d20 Mon Sep 17 00:00:00 2001 From: scturtle Date: Tue, 18 Aug 2015 17:41:14 +0800 Subject: [PATCH 065/147] Refactor the hard-coding "dist" --- Language/Haskell/GhcMod/CabalHelper.hs | 33 ++++++++++++------------ Language/Haskell/GhcMod/Cradle.hs | 5 ++++ Language/Haskell/GhcMod/PathsAndFiles.hs | 14 +++++++--- Language/Haskell/GhcMod/Target.hs | 22 +++++++++------- Language/Haskell/GhcMod/Types.hs | 2 ++ 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index f01a769..478b9e8 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -53,24 +53,24 @@ import Paths_ghc_mod as GhcMod -- access home modules getGhcMergedPkgOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) => m [GHCOption] -getGhcMergedPkgOptions = chCached Cached { +getGhcMergedPkgOptions = chCached $ \distDir -> Cached { cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), - cacheFile = mergedPkgOptsCacheFile, + cacheFile = distDir mergedPkgOptsCacheFile, cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do readProc <- gmReadProcess opts <- withCabal $ runQuery'' readProc progs rootdir distdir $ ghcMergedPkgOptions - return ([setupConfigPath], opts) + return ([distDir setupConfigPath], opts) } getCabalPackageDbStack :: (IOish m, GmEnv m, GmState m, GmLog m) => m [GhcPkgDb] -getCabalPackageDbStack = chCached Cached { +getCabalPackageDbStack = chCached $ \distDir -> Cached { cacheLens = Just (lGmcPackageDbStack . lGmCaches), - cacheFile = pkgDbStackCacheFile, + cacheFile = distDir pkgDbStackCacheFile, cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do readProc <- gmReadProcess dbs <- withCabal $ map chPkgToGhcPkg <$> runQuery'' readProc progs rootdir distdir packageDbStack - return ([setupConfigPath, sandboxConfigFile], dbs) + return ([distDir setupConfigPath, sandboxConfigFile], dbs) } chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb @@ -85,9 +85,9 @@ chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f -- 'resolveGmComponents'. getComponents :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) => m [GmComponent 'GMCRaw ChEntrypoint] -getComponents = chCached Cached { +getComponents = chCached$ \distDir -> Cached { cacheLens = Just (lGmcComponents . lGmCaches), - cacheFile = cabalHelperCacheFile, + cacheFile = distDir cabalHelperCacheFile, cachedAction = \ _tcf (progs, rootdir, distdir, _vers) _ma -> do readProc <- gmReadProcess runQuery'' readProc progs rootdir distdir $ do @@ -100,7 +100,7 @@ getComponents = chCached Cached { <*> entrypoints <*> sourceDirs let cs = flip map q $ curry8 (GmComponent mempty) - return ([setupConfigPath], cs) + return ([distDir setupConfigPath], cs) } where curry8 fn (a, (b, (c, (d, (e, (f, (g, h))))))) = fn a b c d e f g h @@ -117,7 +117,7 @@ prepareCabalHelper :: (IOish m, GmEnv m, GmLog m) => m () prepareCabalHelper = do crdl <- cradle let projdir = cradleRootDir crdl - distdir = projdir "dist" + distdir = projdir cradleDistDir crdl readProc <- gmReadProcess when (cradleProjectType crdl == CabalProject) $ withCabal $ liftIO $ prepare readProc projdir distdir @@ -151,7 +151,7 @@ withCabal action = do readProc <- gmReadProcess let projdir = cradleRootDir crdl - distdir = projdir "dist" + distdir = projdir cradleDistDir crdl mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl) @@ -222,17 +222,18 @@ helperProgs opts = Programs { } chCached :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m, Serialize a) - => Cached m GhcModState ChCacheData a -> m a + => (FilePath -> Cached m GhcModState ChCacheData a) -> m a chCached c = do root <- cradleRootDir <$> cradle - d <- cacheInputData root - withCabal $ cached root c d + dist <- cradleDistDir <$> cradle + d <- cacheInputData root dist + withCabal $ cached root (c dist) d where - cacheInputData root = do + cacheInputData root dist = do opt <- options return $ ( helperProgs opt , root - , root "dist" + , root dist , (gmVer, chVer) ) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 15c8981..43325e1 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -65,6 +65,7 @@ cabalCradle wdir = do , cradleRootDir = cabalDir , cradleTempDir = error "tmpDir" , cradleCabalFile = Just cabalFile + , cradleDistDir = "dist" } stackCradle :: FilePath -> MaybeT IO Cradle @@ -74,6 +75,7 @@ stackCradle wdir = do let cabalDir = takeDirectory cabalFile _stackConfigFile <- MaybeT $ findStackConfigFile cabalDir + distDir <- liftIO $ findStackDistDir cabalDir return Cradle { cradleProjectType = StackProject @@ -81,6 +83,7 @@ stackCradle wdir = do , cradleRootDir = cabalDir , cradleTempDir = error "tmpDir" , cradleCabalFile = Just cabalFile + , cradleDistDir = distDir } sandboxCradle :: FilePath -> MaybeT IO Cradle @@ -92,6 +95,7 @@ sandboxCradle wdir = do , cradleRootDir = sbDir , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing + , cradleDistDir = "dist" } plainCradle :: FilePath -> MaybeT IO Cradle @@ -102,4 +106,5 @@ plainCradle wdir = do , cradleRootDir = wdir , cradleTempDir = error "tmpDir" , cradleCabalFile = Nothing + , cradleDistDir = "dist" } diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 7f9249d..5a0d881 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -74,6 +74,14 @@ findCabalFile dir = do findStackConfigFile :: FilePath -> IO (Maybe FilePath) findStackConfigFile dir = mightExist (dir "stack.yaml") +findStackDistDir :: FilePath -> IO FilePath +findStackDistDir dir = U.withDirectory_ dir $ do + mstack <- liftIO $ findExecutable "stack" + case mstack of + Nothing -> return "dist" + Just stack -> + takeWhile (/='\n') <$> readProcess stack ["path", "--dist-dir"] "" + -- | Get path to sandbox config file getSandboxDb :: FilePath -- ^ Path to the cabal package root directory (containing the @@ -182,17 +190,17 @@ parents dir' = ---------------------------------------------------------------- setupConfigFile :: Cradle -> FilePath -setupConfigFile crdl = cradleRootDir crdl setupConfigPath +setupConfigFile crdl = cradleRootDir crdl cradleDistDir crdl setupConfigPath sandboxConfigFile :: FilePath sandboxConfigFile = "cabal.sandbox.config" -- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@ setupConfigPath :: FilePath -setupConfigPath = "dist/setup-config" -- localBuildInfoFile defaultDistPref +setupConfigPath = "setup-config" -- localBuildInfoFile defaultDistPref macrosHeaderPath :: FilePath -macrosHeaderPath = "dist/build/autogen/cabal_macros.h" +macrosHeaderPath = "build/autogen/cabal_macros.h" ghcSandboxPkgDbDir :: String -> String ghcSandboxPkgDbDir buildPlatf = do diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index eb92204..2f6fbb7 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -178,12 +178,13 @@ targetGhcOptions crdl sefnmn = do let cn = pickComponent candidates return $ gmcGhcOpts $ fromJust $ Map.lookup cn mcs -resolvedComponentsCache :: IOish m => Cached (GhcModT m) GhcModState +resolvedComponentsCache :: IOish m => FilePath -> + Cached (GhcModT m) GhcModState [GmComponent 'GMCRaw (Set.Set ModulePath)] (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath))) -resolvedComponentsCache = Cached { +resolvedComponentsCache distDir = Cached { cacheLens = Just (lGmcResolvedComponents . lGmCaches), - cacheFile = resolvedComponentsCacheFile, + cacheFile = distDir resolvedComponentsCacheFile, cachedAction = \tcfs comps ma -> do Cradle {..} <- cradle let iifsM = invalidatingInputFiles tcfs @@ -194,13 +195,13 @@ resolvedComponentsCache = Cached { Just iifs -> let filterOutSetupCfg = - filter (/= cradleRootDir setupConfigPath) + filter (/= cradleRootDir cradleDistDir setupConfigPath) changedFiles = filterOutSetupCfg iifs in if null changedFiles then Nothing else Just $ map Left changedFiles setupChanged = maybe False - (elem $ cradleRootDir setupConfigPath) + (elem $ cradleRootDir cradleDistDir setupConfigPath) iifsM case (setupChanged, ma) of (False, Just mcs) -> gmsGet >>= \s -> gmsPut s { gmComponents = mcs } @@ -217,7 +218,7 @@ resolvedComponentsCache = Cached { text "files changed" <+>: changedDoc mcs <- resolveGmComponents mums comps - return (setupConfigPath:flatten mcs , mcs) + return ((cradleDistDir setupConfigPath) : flatten mcs , mcs) } where @@ -298,7 +299,8 @@ resolveGmComponent :: (IOish m, GmLog m, GmEnv m, GmState m) -> GmComponent 'GMCRaw (Set ModulePath) -> m (GmComponent 'GMCResolved (Set ModulePath)) resolveGmComponent mums c@GmComponent {..} = do - withLightHscEnv ghcOpts $ \env -> do + distDir <- cradleDistDir <$> cradle + withLightHscEnv (ghcOpts distDir) $ \env -> do let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs let mg = gmcHomeModuleGraph let simp = gmcEntrypoints @@ -312,10 +314,10 @@ resolveGmComponent mums c@GmComponent {..} = do return $ c { gmcEntrypoints = simp, gmcHomeModuleGraph = mg' } - where ghcOpts = concat [ + where ghcOpts distDir = concat [ gmcGhcSrcOpts, gmcGhcLangOpts, - [ "-optP-include", "-optP" ++ macrosHeaderPath ] + [ "-optP-include", "-optP" ++ distDir macrosHeaderPath ] ] resolveEntrypoint :: (IOish m, GmEnv m, GmLog m, GmState m) @@ -482,4 +484,4 @@ cabalResolvedComponents :: (IOish m) => cabalResolvedComponents = do crdl@(Cradle{..}) <- cradle comps <- mapM (resolveEntrypoint crdl) =<< getComponents - cached cradleRootDir resolvedComponentsCache comps + cached cradleRootDir (resolvedComponentsCache cradleDistDir) comps diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index b3c99ea..8525503 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -135,6 +135,8 @@ data Cradle = Cradle { , cradleTempDir :: FilePath -- | The file name of the found cabal file. , cradleCabalFile :: Maybe FilePath + -- | The build info directory. + , cradleDistDir :: FilePath } deriving (Eq, Show) From d660e7cd85dbf48063d97639b4ea28285b06895e Mon Sep 17 00:00:00 2001 From: scturtle Date: Tue, 18 Aug 2015 20:55:45 +0800 Subject: [PATCH 066/147] add 'StackProject' around --- Language/Haskell/GhcMod/CabalHelper.hs | 2 +- Language/Haskell/GhcMod/Debug.hs | 1 + src/GHCMod.hs | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 478b9e8..d269bb4 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -119,7 +119,7 @@ prepareCabalHelper = do let projdir = cradleRootDir crdl distdir = projdir cradleDistDir crdl readProc <- gmReadProcess - when (cradleProjectType crdl == CabalProject) $ + when (cradleProjectType crdl == CabalProject || cradleProjectType crdl == StackProject) $ withCabal $ liftIO $ prepare readProc projdir distdir parseCustomPackageDb :: String -> [GhcPkgDb] diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 54e85d2..2441ec9 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -27,6 +27,7 @@ debugInfo = do cabal <- case cradleProjectType of CabalProject -> cabalDebug + StackProject -> cabalDebug _ -> return [] pkgOpts <- packageGhcOptions diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 8bb8934..0b97096 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -669,9 +669,10 @@ nukeCaches = do chdir <- liftIO $ ( "cabal-helper") <$> getAppUserDataDirectory "ghc-mod" c <- cradle - when (cradleProjectType c == CabalProject) $ do + when (cradleProjectType c == CabalProject || cradleProjectType c == StackProject) $ do let root = cradleRootDir c - liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root "dist"] + let dist = cradleDistDir c + liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root dist] trySome :: IO a -> IO (Either SomeException a) trySome = try From 78bdf86a952753b9381c8f50d95002bc97d700cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 19 Aug 2015 06:48:27 +0200 Subject: [PATCH 067/147] Fix all the stack related things --- .travis.yml | 1 + Language/Haskell/GhcMod/CabalHelper.hs | 31 +++++++++-------- Language/Haskell/GhcMod/Caching/Types.hs | 2 +- Language/Haskell/GhcMod/Cradle.hs | 2 +- Language/Haskell/GhcMod/PathsAndFiles.hs | 40 ++++++++++++---------- Language/Haskell/GhcMod/Target.hs | 10 +++--- ghc-mod.cabal | 9 ++--- test/CabalHelperSpec.hs | 7 ++-- test/Main.hs | 22 +++++++----- test/data/stack-project/new-template.cabal | 5 +-- test/data/stack-project/stack.yaml | 5 +++ 11 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 test/data/stack-project/stack.yaml diff --git a/.travis.yml b/.travis.yml index d8d96ad..d836c9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ cache: directories: - ~/.cabal - ~/.ghc + - ~/.stack before_cache: - rm -f $HOME/.cabal/logs $HOME/.cabal/packages/*/build-reports.log diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index d269bb4..2b77ec6 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -53,24 +53,24 @@ import Paths_ghc_mod as GhcMod -- access home modules getGhcMergedPkgOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) => m [GHCOption] -getGhcMergedPkgOptions = chCached $ \distDir -> Cached { +getGhcMergedPkgOptions = chCached $ \distdir -> Cached { cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), - cacheFile = distDir mergedPkgOptsCacheFile, - cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do + cacheFile = mergedPkgOptsCacheFile distdir, + cachedAction = \ _tcf (progs, rootdir, _) _ma -> do readProc <- gmReadProcess opts <- withCabal $ runQuery'' readProc progs rootdir distdir $ ghcMergedPkgOptions - return ([distDir setupConfigPath], opts) + return ([setupConfigPath distdir], opts) } getCabalPackageDbStack :: (IOish m, GmEnv m, GmState m, GmLog m) => m [GhcPkgDb] -getCabalPackageDbStack = chCached $ \distDir -> Cached { +getCabalPackageDbStack = chCached $ \distdir -> Cached { cacheLens = Just (lGmcPackageDbStack . lGmCaches), - cacheFile = distDir pkgDbStackCacheFile, - cachedAction = \ _tcf (progs, rootdir, distdir, _) _ma -> do + cacheFile = pkgDbStackCacheFile distdir, + cachedAction = \ _tcf (progs, rootdir, _) _ma -> do readProc <- gmReadProcess dbs <- withCabal $ map chPkgToGhcPkg <$> runQuery'' readProc progs rootdir distdir packageDbStack - return ([distDir setupConfigPath, sandboxConfigFile], dbs) + return ([setupConfigPath distdir, sandboxConfigFile], dbs) } chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb @@ -85,10 +85,10 @@ chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f -- 'resolveGmComponents'. getComponents :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) => m [GmComponent 'GMCRaw ChEntrypoint] -getComponents = chCached$ \distDir -> Cached { +getComponents = chCached$ \distdir -> Cached { cacheLens = Just (lGmcComponents . lGmCaches), - cacheFile = distDir cabalHelperCacheFile, - cachedAction = \ _tcf (progs, rootdir, distdir, _vers) _ma -> do + cacheFile = cabalHelperCacheFile distdir, + cachedAction = \ _tcf (progs, rootdir, _vers) _ma -> do readProc <- gmReadProcess runQuery'' readProc progs rootdir distdir $ do q <- join7 @@ -100,7 +100,7 @@ getComponents = chCached$ \distDir -> Cached { <*> entrypoints <*> sourceDirs let cs = flip map q $ curry8 (GmComponent mempty) - return ([distDir setupConfigPath], cs) + return ([setupConfigPath distdir], cs) } where curry8 fn (a, (b, (c, (d, (e, (f, (g, h))))))) = fn a b c d e f g h @@ -226,14 +226,15 @@ chCached :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m, Serialize a) chCached c = do root <- cradleRootDir <$> cradle dist <- cradleDistDir <$> cradle - d <- cacheInputData root dist + d <- cacheInputData root withCabal $ cached root (c dist) d where - cacheInputData root dist = do + -- we don't need to include the disdir in the cache input because when it + -- changes the cache files will be gone anyways ;) + cacheInputData root = do opt <- options return $ ( helperProgs opt , root - , root dist , (gmVer, chVer) ) diff --git a/Language/Haskell/GhcMod/Caching/Types.hs b/Language/Haskell/GhcMod/Caching/Types.hs index c4fce4c..2a21b1a 100644 --- a/Language/Haskell/GhcMod/Caching/Types.hs +++ b/Language/Haskell/GhcMod/Caching/Types.hs @@ -49,4 +49,4 @@ data TimedCacheFiles = TimedCacheFiles { -- ^ Timestamped files returned by the cached action } deriving (Eq, Ord, Show) -type ChCacheData = (Programs, FilePath, FilePath, (Version, [Char])) +type ChCacheData = (Programs, FilePath, (Version, [Char])) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 43325e1..e514e5b 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -75,7 +75,7 @@ stackCradle wdir = do let cabalDir = takeDirectory cabalFile _stackConfigFile <- MaybeT $ findStackConfigFile cabalDir - distDir <- liftIO $ findStackDistDir cabalDir + distDir <- MaybeT $ getStackDistDir cabalDir return Cradle { cradleProjectType = StackProject diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 5a0d881..f596ccd 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -22,6 +22,7 @@ module Language.Haskell.GhcMod.PathsAndFiles ( import Config (cProjectVersion) import Control.Applicative import Control.Monad +import Control.Monad.Trans.Maybe import Data.List import Data.Char import Data.Maybe @@ -74,13 +75,10 @@ findCabalFile dir = do findStackConfigFile :: FilePath -> IO (Maybe FilePath) findStackConfigFile dir = mightExist (dir "stack.yaml") -findStackDistDir :: FilePath -> IO FilePath -findStackDistDir dir = U.withDirectory_ dir $ do - mstack <- liftIO $ findExecutable "stack" - case mstack of - Nothing -> return "dist" - Just stack -> - takeWhile (/='\n') <$> readProcess stack ["path", "--dist-dir"] "" +getStackDistDir :: FilePath -> IO (Maybe FilePath) +getStackDistDir dir = U.withDirectory_ dir $ runMaybeT $ do + stack <- MaybeT $ findExecutable "stack" + liftIO $ takeWhile (/='\n') <$> readProcess stack ["path", "--dist-dir"] "" -- | Get path to sandbox config file getSandboxDb :: FilePath @@ -190,14 +188,16 @@ parents dir' = ---------------------------------------------------------------- setupConfigFile :: Cradle -> FilePath -setupConfigFile crdl = cradleRootDir crdl cradleDistDir crdl setupConfigPath +setupConfigFile crdl = + cradleRootDir crdl setupConfigPath (cradleDistDir crdl) sandboxConfigFile :: FilePath sandboxConfigFile = "cabal.sandbox.config" -- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@ -setupConfigPath :: FilePath -setupConfigPath = "setup-config" -- localBuildInfoFile defaultDistPref +setupConfigPath :: FilePath -> FilePath +setupConfigPath dist = dist "setup-config" + -- localBuildInfoFile defaultDistPref macrosHeaderPath :: FilePath macrosHeaderPath = "build/autogen/cabal_macros.h" @@ -216,17 +216,21 @@ symbolCache crdl = cradleTempDir crdl symbolCacheFile symbolCacheFile :: String symbolCacheFile = "ghc-mod.symbol-cache" -resolvedComponentsCacheFile :: String -resolvedComponentsCacheFile = setupConfigPath <.> "ghc-mod.resolved-components" +resolvedComponentsCacheFile :: FilePath -> FilePath +resolvedComponentsCacheFile dist = + setupConfigPath dist <.> "ghc-mod.resolved-components" -cabalHelperCacheFile :: String -cabalHelperCacheFile = setupConfigPath <.> "ghc-mod.cabal-components" +cabalHelperCacheFile :: FilePath -> FilePath +cabalHelperCacheFile dist = + setupConfigPath dist <.> "ghc-mod.cabal-components" -mergedPkgOptsCacheFile :: String -mergedPkgOptsCacheFile = setupConfigPath <.> "ghc-mod.package-options" +mergedPkgOptsCacheFile :: FilePath -> FilePath +mergedPkgOptsCacheFile dist = + setupConfigPath dist <.> "ghc-mod.package-options" -pkgDbStackCacheFile :: String -pkgDbStackCacheFile = setupConfigPath <.> "ghc-mod.package-db-stack" +pkgDbStackCacheFile :: FilePath -> FilePath +pkgDbStackCacheFile dist = + setupConfigPath dist <.> "ghc-mod.package-db-stack" -- | @findCustomPackageDbFile dir@. Searches for a @.ghc-mod.cradle@ file in @dir@. -- If it exists in the given directory it is returned otherwise @findCradleFile@ returns @Nothing@ diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 2f6fbb7..7fe9697 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -182,9 +182,9 @@ resolvedComponentsCache :: IOish m => FilePath -> Cached (GhcModT m) GhcModState [GmComponent 'GMCRaw (Set.Set ModulePath)] (Map.Map ChComponentName (GmComponent 'GMCResolved (Set.Set ModulePath))) -resolvedComponentsCache distDir = Cached { +resolvedComponentsCache distdir = Cached { cacheLens = Just (lGmcResolvedComponents . lGmCaches), - cacheFile = distDir resolvedComponentsCacheFile, + cacheFile = resolvedComponentsCacheFile distdir, cachedAction = \tcfs comps ma -> do Cradle {..} <- cradle let iifsM = invalidatingInputFiles tcfs @@ -195,13 +195,13 @@ resolvedComponentsCache distDir = Cached { Just iifs -> let filterOutSetupCfg = - filter (/= cradleRootDir cradleDistDir setupConfigPath) + filter (/= cradleRootDir setupConfigPath distdir) changedFiles = filterOutSetupCfg iifs in if null changedFiles then Nothing else Just $ map Left changedFiles setupChanged = maybe False - (elem $ cradleRootDir cradleDistDir setupConfigPath) + (elem $ cradleRootDir setupConfigPath distdir) iifsM case (setupChanged, ma) of (False, Just mcs) -> gmsGet >>= \s -> gmsPut s { gmComponents = mcs } @@ -218,7 +218,7 @@ resolvedComponentsCache distDir = Cached { text "files changed" <+>: changedDoc mcs <- resolveGmComponents mums comps - return ((cradleDistDir setupConfigPath) : flatten mcs , mcs) + return (setupConfigPath distdir : flatten mcs , mcs) } where diff --git a/ghc-mod.cabal b/ghc-mod.cabal index ab2d1ae..595658c 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -85,11 +85,12 @@ Extra-Source-Files: ChangeLog test/data/file-mapping/preprocessor/*.hs test/data/file-mapping/lhs/*.lhs test/data/nice-qualification/*.hs + test/data/stack-project/stack.yaml test/data/stack-project/new-template.cabal - test/data/stack-project/Setup.hs - test/data/stack-project/app/Main.hs - test/data/stack-project/src/Lib.hs - test/data/stack-project/test/Spec.hs + test/data/stack-project/*.hs + test/data/stack-project/app/*.hs + test/data/stack-project/src/*.hs + test/data/stack-project/test/*.hs Library Default-Language: Haskell2010 diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 69e3814..231fc3c 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -58,10 +58,9 @@ spec = do it "handles stack project" $ do let tdir = "test/data/stack-project" - opts <- map gmcGhcOpts <$> runD' tdir getComponents - let ghcOpts = head opts - pkgs = pkgOptions ghcOpts - pkgs `shouldBe` ["Cabal","base","new-template"] + [ghcOpts] <- map gmcGhcOpts . filter ((==ChExeName "new-template-exe") . gmcName) <$> runD' tdir getComponents + let pkgs = pkgOptions ghcOpts + pkgs `shouldBe` ["base", "bytestring"] it "extracts build dependencies" $ do let tdir = "test/data/cabal-project" diff --git a/test/Main.hs b/test/Main.hs index b9827d0..2e9f558 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -28,12 +28,6 @@ main = do genSandboxCfg `mapM_` sandboxes genGhcPkgCache `mapM_` pkgDirs - let stackDir = "test/data/stack-project" - void $ withDirectory_ stackDir $ do - void $ system "stack init --force" - void $ system "stack setup" - void $ system "stack build" - let caches = [ "setup-config" , "setup-config.ghc-mod.cabal-helper" , "setup-config.ghc-mod.cabal-components" @@ -42,16 +36,26 @@ main = do , "setup-config.ghc-mod.package-db-stack" , "ghc-mod.cache" ] - cachesFindExp :: String - cachesFindExp = unwords $ intersperse "-o " $ map ("-name "++) caches + findExp = unwords $ intersperse "-o " $ concat [ + stackWorkFindExp, + cachesFindExp + ] + cachesFindExp = map ("-name "++) caches + stackWorkFindExp = ["-name .stack-work -type d"] - cleanCmd = "find test \\( "++ cachesFindExp ++" \\) -exec rm {} \\;" + cleanCmd = "find test \\( "++ findExp ++" \\) -exec rm -r {} \\;" putStrLn $ "$ " ++ cleanCmd void $ system cleanCmd void $ system "cabal --version" void $ system "ghc --version" + let stackDir = "test/data/stack-project" + void $ withDirectory_ stackDir $ do +-- void $ system "stack init --force" + void $ system "stack setup" + void $ system "stack build" + (putStrLn =<< runD debugInfo) `E.catch` (\(_ :: E.SomeException) -> return () ) diff --git a/test/data/stack-project/new-template.cabal b/test/data/stack-project/new-template.cabal index 34a3a0b..c71f211 100644 --- a/test/data/stack-project/new-template.cabal +++ b/test/data/stack-project/new-template.cabal @@ -7,10 +7,10 @@ homepage: http://github.com/name/project -- license-file: LICENSE author: Your name here maintainer: your.address@example.com --- copyright: +-- copyright: category: Web build-type: Simple --- extra-source-files: +-- extra-source-files: cabal-version: >=1.10 library @@ -25,6 +25,7 @@ executable new-template-exe ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base , new-template + , bytestring default-language: Haskell2010 test-suite new-template-test diff --git a/test/data/stack-project/stack.yaml b/test/data/stack-project/stack.yaml new file mode 100644 index 0000000..6252712 --- /dev/null +++ b/test/data/stack-project/stack.yaml @@ -0,0 +1,5 @@ +flags: {} +packages: +- '.' +extra-deps: [] +resolver: lts-2.17 From 18a8c67d39230925579f6a68b40f6942d12761b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 19 Aug 2015 08:46:56 +0200 Subject: [PATCH 068/147] Take sandbox cfg into account for caches --- Language/Haskell/GhcMod/CabalHelper.hs | 23 +++++++++++++++-------- Language/Haskell/GhcMod/Cradle.hs | 9 +++++++-- Language/Haskell/GhcMod/GhcPkg.hs | 2 +- Language/Haskell/GhcMod/PathsAndFiles.hs | 18 +++++++++--------- Language/Haskell/GhcMod/Target.hs | 12 ++++-------- Language/Haskell/GhcMod/World.hs | 3 +++ test/PathsAndFilesSpec.hs | 11 ++++++++--- 7 files changed, 47 insertions(+), 31 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 2b77ec6..78e1d7c 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -68,9 +68,10 @@ getCabalPackageDbStack = chCached $ \distdir -> Cached { cacheLens = Just (lGmcPackageDbStack . lGmCaches), cacheFile = pkgDbStackCacheFile distdir, cachedAction = \ _tcf (progs, rootdir, _) _ma -> do + crdl <- cradle readProc <- gmReadProcess dbs <- withCabal $ map chPkgToGhcPkg <$> runQuery'' readProc progs rootdir distdir packageDbStack - return ([setupConfigPath distdir, sandboxConfigFile], dbs) + return ([setupConfigFile crdl, sandboxConfigFile crdl], dbs) } chPkgToGhcPkg :: ChPkgDb -> GhcPkgDb @@ -153,8 +154,9 @@ withCabal action = do let projdir = cradleRootDir crdl distdir = projdir cradleDistDir crdl - mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl - mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl) + mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl + mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl) + mCabalSandboxConfig <- liftIO $ timeMaybe (sandboxConfigFile crdl) mCusPkgDbStack <- getCustomPkgDbStack @@ -173,11 +175,17 @@ withCabal action = do when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $ gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project." + + when (isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ + gmLog GmDebug "" $ strDoc $ "sandbox configuration is out of date, reconfiguring Cabal project." + when pkgDbStackOutOfSync $ gmLog GmDebug "" $ strDoc $ "package-db stack out of sync with ghc-mod.package-db-stack, reconfiguring Cabal project." - when (isSetupConfigOutOfDate mCabalFile mCabalConfig || pkgDbStackOutOfSync) $ - withDirectory_ (cradleRootDir crdl) $ do + when ( isSetupConfigOutOfDate mCabalFile mCabalConfig + || pkgDbStackOutOfSync + || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ + withDirectory_ (cradleRootDir crdl) $ do let progOpts = [ "--with-ghc=" ++ T.ghcProgram opts ] -- Only pass ghc-pkg if it was actually set otherwise we @@ -200,9 +208,9 @@ pkgDbArg (PackageDb p) = "--package-db=" ++ p -- @Nothing < Nothing = False@ -- (since we don't need to @cabal configure@ when no cabal file exists.) -- --- * Cabal file doesn't exist (unlikely case) -> should return False +-- * Cabal file doesn't exist (impossible since cabal-helper is only used with +-- cabal projects) -> should return False -- @Just cc < Nothing = False@ --- TODO: should we delete dist/setup-config? -- -- * dist/setup-config doesn't exist yet -> should return True: -- @Nothing < Just cf = True@ @@ -213,7 +221,6 @@ isSetupConfigOutOfDate :: Maybe TimedFile -> Maybe TimedFile -> Bool isSetupConfigOutOfDate worldCabalFile worldCabalConfig = do worldCabalConfig < worldCabalFile - helperProgs :: Options -> Programs helperProgs opts = Programs { cabalProgram = T.cabalProgram opts, diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index e514e5b..c68ce21 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -1,9 +1,14 @@ -module Language.Haskell.GhcMod.Cradle ( +{-# LANGUAGE CPP #-} +module Language.Haskell.GhcMod.Cradle +#ifndef SPEC + ( findCradle , findCradle' , findSpecCradle , cleanupCradle - ) where + ) +#endif + where import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Monad.Types diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index 86255fc..df2bce2 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -67,7 +67,7 @@ getPackageDbStack = do PlainProject -> return [GlobalDb, UserDb] SandboxProject -> do - Just db <- liftIO $ getSandboxDb $ cradleRootDir crdl + Just db <- liftIO $ getSandboxDb crdl return $ [GlobalDb, db] CabalProject -> getCabalPackageDbStack diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index f596ccd..0639e24 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -81,12 +81,9 @@ getStackDistDir dir = U.withDirectory_ dir $ runMaybeT $ do liftIO $ takeWhile (/='\n') <$> readProcess stack ["path", "--dist-dir"] "" -- | Get path to sandbox config file -getSandboxDb :: FilePath - -- ^ Path to the cabal package root directory (containing the - -- @cabal.sandbox.config@ file) - -> IO (Maybe GhcPkgDb) -getSandboxDb d = do - mConf <- traverse readFile =<< mightExist (d "cabal.sandbox.config") +getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) +getSandboxDb crdl = do + mConf <-traverse readFile =<< mightExist (sandboxConfigFile crdl) bp <- buildPlatform readProcess return $ PackageDb . fixPkgDbVer bp <$> (extractSandboxDbDir =<< mConf) @@ -154,7 +151,7 @@ findCabalSandboxDir dir = do _ -> Nothing where - isSandboxConfig = (==sandboxConfigFile) + isSandboxConfig = (==sandboxConfigFileName) zipMapM :: Monad m => (a -> m c) -> [a] -> m [(a,c)] zipMapM f as = mapM (\a -> liftM ((,) a) $ f a) as @@ -191,8 +188,11 @@ setupConfigFile :: Cradle -> FilePath setupConfigFile crdl = cradleRootDir crdl setupConfigPath (cradleDistDir crdl) -sandboxConfigFile :: FilePath -sandboxConfigFile = "cabal.sandbox.config" +sandboxConfigFile :: Cradle -> FilePath +sandboxConfigFile crdl = cradleRootDir crdl sandboxConfigFileName + +sandboxConfigFileName :: String +sandboxConfigFileName = "cabal.sandbox.config" -- | Path to 'LocalBuildInfo' file, usually @dist/setup-config@ setupConfigPath :: FilePath -> FilePath diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 7fe9697..be5433e 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -280,19 +280,15 @@ stackOpts crdl = do -- also works for plain projects! sandboxOpts :: MonadIO m => Cradle -> m [String] sandboxOpts crdl = do - pkgDbStack <- liftIO $ getSandboxPackageDbStack $ cradleRootDir crdl + pkgDbStack <- liftIO $ getSandboxPackageDbStack let pkgOpts = ghcDbStackOpts pkgDbStack return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"] where (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) - getSandboxPackageDbStack - :: FilePath - -- ^ Project Directory (where the cabal.sandbox.config file would be if - -- it exists) - -> IO [GhcPkgDb] - getSandboxPackageDbStack cdir = - ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb cdir + getSandboxPackageDbStack :: IO [GhcPkgDb] + getSandboxPackageDbStack = + ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb crdl resolveGmComponent :: (IOish m, GmLog m, GmEnv m, GmState m) => Maybe [CompilationUnit] -- ^ Updated modules diff --git a/Language/Haskell/GhcMod/World.hs b/Language/Haskell/GhcMod/World.hs index e887990..0d413e5 100644 --- a/Language/Haskell/GhcMod/World.hs +++ b/Language/Haskell/GhcMod/World.hs @@ -18,6 +18,7 @@ data World = World { worldPackageCaches :: [TimedFile] , worldCabalFile :: Maybe TimedFile , worldCabalConfig :: Maybe TimedFile + , worldCabalSandboxConfig :: Maybe TimedFile , worldSymbolCache :: Maybe TimedFile } deriving (Eq, Show) @@ -33,12 +34,14 @@ getCurrentWorld = do pkgCaches <- timedPackageCaches mCabalFile <- liftIO $ timeFile `traverse` cradleCabalFile crdl mCabalConfig <- liftIO $ timeMaybe (setupConfigFile crdl) + mCabalSandboxConfig <- liftIO $ timeMaybe (sandboxConfigFile crdl) mSymbolCache <- liftIO $ timeMaybe (symbolCache crdl) return World { worldPackageCaches = pkgCaches , worldCabalFile = mCabalFile , worldCabalConfig = mCabalConfig + , worldCabalSandboxConfig = mCabalSandboxConfig , worldSymbolCache = mSymbolCache } diff --git a/test/PathsAndFilesSpec.hs b/test/PathsAndFilesSpec.hs index 1e15b1d..cb34aa4 100644 --- a/test/PathsAndFilesSpec.hs +++ b/test/PathsAndFilesSpec.hs @@ -1,7 +1,10 @@ module PathsAndFilesSpec where -import Language.Haskell.GhcMod.PathsAndFiles +import Language.Haskell.GhcMod.PathsAndFiles +import Language.Haskell.GhcMod.Cradle + +import Control.Monad.Trans.Maybe import System.Directory import System.FilePath import Test.Hspec @@ -12,11 +15,13 @@ spec = do describe "getSandboxDb" $ do it "can parse a config file and extract the sandbox package-db" $ do cwd <- getCurrentDirectory - Just db <- getSandboxDb "test/data/cabal-project" + Just crdl <- runMaybeT $ plainCradle "test/data/cabal-project" + Just db <- getSandboxDb crdl db `shouldSatisfy` isPkgDbAt (cwd "test/data/cabal-project/.cabal-sandbox") it "returns Nothing if the sandbox config file is broken" $ do - getSandboxDb "test/data/broken-sandbox" `shouldReturn` Nothing + Just crdl <- runMaybeT $ plainCradle "test/data/broken-sandbox" + getSandboxDb crdl `shouldReturn` Nothing describe "findCabalFile" $ do it "works" $ do From 23a48aa6c728496e7971428e76ddc6e639901e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 19 Aug 2015 09:04:25 +0200 Subject: [PATCH 069/147] Use cabal-helper for stack --- Language/Haskell/GhcMod/CabalHelper.hs | 42 +++++++++++++++++--------- Language/Haskell/GhcMod/Target.hs | 12 ++------ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 78e1d7c..fac9d1a 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -169,9 +169,7 @@ withCabal action = do Nothing -> return False - cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack - - --TODO: also invalidate when sandboxConfig file changed + projType <- cradleProjectType <$> cradle when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $ gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project." @@ -185,20 +183,34 @@ withCabal action = do when ( isSetupConfigOutOfDate mCabalFile mCabalConfig || pkgDbStackOutOfSync || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ - withDirectory_ (cradleRootDir crdl) $ do - let progOpts = - [ "--with-ghc=" ++ T.ghcProgram opts ] - -- Only pass ghc-pkg if it was actually set otherwise we - -- might break cabal's guessing logic - ++ if T.ghcPkgProgram opts /= T.ghcPkgProgram defaultOptions - then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram opts ] - else [] - ++ map pkgDbArg cusPkgStack - liftIO $ void $ readProc (T.cabalProgram opts) ("configure":progOpts) "" - gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" - liftIO $ writeAutogenFiles readProc projdir distdir + case projType of + CabalProject -> + cabalReconfigure readProc opts crdl projdir distdir + StackProject -> + -- https://github.com/commercialhaskell/stack/issues/820 + gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build'" + _ -> + error $ "withCabal: unsupported project type: " ++ show projType + action + where + cabalReconfigure readProc opts crdl projdir distdir = do + withDirectory_ (cradleRootDir crdl) $ do + cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack + let progOpts = + [ "--with-ghc=" ++ T.ghcProgram opts ] + -- Only pass ghc-pkg if it was actually set otherwise we + -- might break cabal's guessing logic + ++ if T.ghcPkgProgram opts /= T.ghcPkgProgram defaultOptions + then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram opts ] + else [] + ++ map pkgDbArg cusPkgStack + liftIO $ void $ readProc (T.cabalProgram opts) ("configure":progOpts) "" + gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" + liftIO $ writeAutogenFiles readProc projdir distdir + + pkgDbArg :: GhcPkgDb -> String pkgDbArg GlobalDb = "--package-db=global" pkgDbArg UserDb = "--package-db=user" diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index be5433e..2a23fc9 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -149,7 +149,7 @@ targetGhcOptions crdl sefnmn = do case cradleProjectType crdl of CabalProject -> cabalOpts crdl - StackProject -> stackOpts crdl + StackProject -> cabalOpts crdl _ -> sandboxOpts crdl where zipMap f l = l `zip` (f `map` l) @@ -266,17 +266,9 @@ packageGhcOptions = do crdl <- cradle case cradleProjectType crdl of CabalProject -> getGhcMergedPkgOptions - StackProject -> stackOpts crdl + StackProject -> getGhcMergedPkgOptions _ -> sandboxOpts crdl -stackOpts :: MonadIO m => Cradle -> m [String] -stackOpts crdl = do - pkgDbStack <- liftIO getStackPackageDbStack - let pkgOpts = ghcDbStackOpts pkgDbStack - return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"] - where - (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) - -- also works for plain projects! sandboxOpts :: MonadIO m => Cradle -> m [String] sandboxOpts crdl = do From 24510719b865127035f35af404bb98c84e1c35af Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:27:27 +0900 Subject: [PATCH 070/147] adding "make lint" for Elisp. --- elisp/Makefile | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/elisp/Makefile b/elisp/Makefile index 96bec8b..8e89dde 100644 --- a/elisp/Makefile +++ b/elisp/Makefile @@ -1,17 +1,17 @@ SRCS = ghc.el ghc-func.el ghc-doc.el ghc-comp.el ghc-check.el ghc-process.el \ ghc-command.el ghc-info.el ghc-ins-mod.el ghc-indent.el ghc-rewrite.el EMACS = emacs -DETECT = xemacs TEMPFILE = temp.el +TEMPFILE2 = temp2.el all: $(TEMPFILE) ghc.el $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE) -f ghc-compile rm -f $(TEMPFILE) -detect: $(TEMPFILE) ghc.el - $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE) -f ghc-compile - rm -f $(DETECT) +lint: $(TEMPFILE2) ghc.el + $(EMACS) -batch -q -no-site-file -l ./$(TEMPFILE2) -f ghc-compile + rm -f $(TEMPFILE2) $(TEMPFILE): @echo '(setq load-path (cons "." load-path))' >> $(TEMPFILE) @@ -19,8 +19,15 @@ $(TEMPFILE): @echo $(SRCS)| sed -e 's/\(ghc[^ ]*\.el\)/"\1"/g' >> $(TEMPFILE) @echo ')))' >> $(TEMPFILE) +$(TEMPFILE2): + @echo '(setq load-path (cons "." load-path))' >> $(TEMPFILE2) + @echo '(setq hack-local-variables-hook (lambda () (setq lexical-binding t)))' >> $(TEMPFILE2) + @echo '(defun ghc-compile () (mapcar (lambda (x) (byte-compile-file x)) (list ' >> $(TEMPFILE2) + @echo $(SRCS)| sed -e 's/\(ghc[^ ]*\.el\)/"\1"/g' >> $(TEMPFILE2) + @echo ')))' >> $(TEMPFILE2) + clean: - rm -f *.elc $(TEMPFILE) + rm -f *.elc $(TEMPFILE) $(TEMPFILE2) VERSION = `grep version ghc.el | sed -e 's/[^0-9\.]//g'` From 531a731da1991c74d675431624fac14ad83ca1a0 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:37:41 +0900 Subject: [PATCH 071/147] preventing "unused variable" warnings. --- elisp/ghc-check.el | 7 +++---- elisp/ghc-doc.el | 2 +- elisp/ghc-func.el | 17 ++++++++++------- elisp/ghc-indent.el | 4 ++-- elisp/ghc-info.el | 2 +- elisp/ghc-process.el | 2 +- elisp/ghc-rewrite.el | 9 +++------ 7 files changed, 21 insertions(+), 22 deletions(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index 5819a17..d02142a 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -295,14 +295,13 @@ nil do not display errors/warnings. (let ((file-msgs (ghc-get-only-holes))) (if (null file-msgs) (message "No holes") - (let ((file (ghc-file-msgs-get-file file-msgs)) - (msgs (ghc-file-msgs-get-msgs file-msgs))) + (let ((msgs (ghc-file-msgs-get-msgs file-msgs))) (ghc-display nil (lambda () (progn (mapc (lambda (x) (insert x "\n\n")) msgs) - (buttonize-buffer)) )))))) + (buttonize-buffer)))))))) (defun ghc-display-holes-to-minibuf () (let ((file-msgs (ghc-get-only-holes))) @@ -479,7 +478,7 @@ nil do not display errors/warnings. (forward-line) (re-search-forward "^$" nil t) (insert fn) - (dotimes (i arity) + (dotimes (_i arity) (insert " _")) (insert " = error \"" fn "\"\n"))))) diff --git a/elisp/ghc-doc.el b/elisp/ghc-doc.el index 512fa7d..42790dd 100644 --- a/elisp/ghc-doc.el +++ b/elisp/ghc-doc.el @@ -54,7 +54,7 @@ (defconst ghc-doc-hackage-format "http://hackage.haskell.org/packages/archive/%s/%s/doc/html/%s.html") -(defun ghc-browse-url-safari (uri &rest args) +(defun ghc-browse-url-safari (uri &rest _args) "Open a URI in Safari using AppleScript. This preserves anchors." (let ((script (format " tell application \"Safari\" diff --git a/elisp/ghc-func.el b/elisp/ghc-func.el index 83d1840..34eff59 100644 --- a/elisp/ghc-func.el +++ b/elisp/ghc-func.el @@ -18,9 +18,10 @@ (defun ghc-replace-character (string from to) "Replace characters equal to FROM to TO in STRING." (let ((ret (copy-sequence string))) - (dotimes (cnt (length ret) ret) + (dotimes (cnt (length ret)) (if (char-equal (aref ret cnt) from) - (aset ret cnt to))))) + (aset ret cnt to))) + ret)) (defun ghc-replace-character-buffer (from-c to-c) (let ((from (char-to-string from-c)) @@ -66,7 +67,7 @@ (dolist (lst lol) (dolist (key lst) (puthash key key hash))) - (maphash (lambda (key val) (ghc-add ret key)) hash) + (maphash (lambda (key _val) (ghc-add ret key)) hash) ret)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -90,8 +91,9 @@ (condition-case nil (let ((m (set-marker (make-marker) 1 (current-buffer))) ret) - (dotimes (i n (nreverse ret)) - (ghc-add ret (read m)))) + (dotimes (_i n) + (ghc-add ret (read m))) + (nreverse ret)) (error ())))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -108,10 +110,11 @@ (defun ghc-keyword-number-pair (spec) (let ((len (length spec)) key ret) - (dotimes (i len (nreverse ret)) + (dotimes (i len) (setq key (intern (concat ":" (symbol-name (car spec))))) (setq ret (cons (cons key i) ret)) - (setq spec (cdr spec))))) + (setq spec (cdr spec))) + (nreverse ret))) (defmacro ghc-defstruct (type &rest spec) `(progn diff --git a/elisp/ghc-indent.el b/elisp/ghc-indent.el index 3f1de9e..519cce5 100644 --- a/elisp/ghc-indent.el +++ b/elisp/ghc-indent.el @@ -10,11 +10,11 @@ (defvar ghc-indent-offset 4) -(defun ghc-make-indent-shallower (beg end) +(defun ghc-make-indent-shallower (_beg _end) (interactive "r") (indent-rigidly (region-beginning) (region-end) (- ghc-indent-offset))) -(defun ghc-make-indent-deeper (beg end) +(defun ghc-make-indent-deeper (_beg _end) (interactive "r") (indent-rigidly (region-beginning) (region-end) ghc-indent-offset)) diff --git a/elisp/ghc-info.el b/elisp/ghc-info.el index d7854c5..abe4356 100644 --- a/elisp/ghc-info.el +++ b/elisp/ghc-info.el @@ -63,7 +63,7 @@ (cons 'ghc-type-clear-overlay after-change-functions)) (add-hook 'post-command-hook 'ghc-type-post-command-hook)) -(defun ghc-type-clear-overlay (&optional beg end len) +(defun ghc-type-clear-overlay (&optional _beg _end _len) (when (overlayp ghc-type-overlay) (ghc-type-set-ix 0) (ghc-type-set-point 0) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 94dc5d7..3e0c4a5 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -141,7 +141,7 @@ (funcall ghc-process-callback 'ng) (setq ghc-process-running nil))))))) -(defun ghc-process-sentinel (process event) +(defun ghc-process-sentinel (_process _event) (setq ghc-process-running nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/elisp/ghc-rewrite.el b/elisp/ghc-rewrite.el index e8087a9..20f7f69 100644 --- a/elisp/ghc-rewrite.el +++ b/elisp/ghc-rewrite.el @@ -126,12 +126,9 @@ (lambda () (insert "Possible completions:\n") (mapc - (lambda (x) - (let* (; (ins1 (insert "- ")) - (pos-begin (point)) - (ins (insert x)) - (pos-end (point)) - (ins3 (insert "\n"))) + (lambda (_x) + (let ((pos-begin (point)) + (pos-end (point))) (make-button pos-begin pos-end :type 'auto-button))) (ghc-sinfo-get-info info)))) (select-window (ghc-auto-completion-window)))) From afe8f69ab98ddf7924d22134e75be331d6722efc Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:39:09 +0900 Subject: [PATCH 072/147] removing unused variable. --- elisp/ghc-doc.el | 1 - 1 file changed, 1 deletion(-) diff --git a/elisp/ghc-doc.el b/elisp/ghc-doc.el index 42790dd..8142a83 100644 --- a/elisp/ghc-doc.el +++ b/elisp/ghc-doc.el @@ -68,7 +68,6 @@ end tell" uri))) (mod- (ghc-replace-character mod ?. ?-)) (ver (ghc-pkg-ver-path-get-ver pkg-ver-path)) (path (ghc-pkg-ver-path-get-path pkg-ver-path)) - (pkg-with-ver (format "%s-%s" pkg ver)) (local (format ghc-doc-local-format path mod-)) (remote (format ghc-doc-hackage-format pkg ver mod-)) (file (format "%s/%s.html" path mod-)) From f0a98cf64f798cc79f82e88f48df6e347faa299d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Wed, 19 Aug 2015 15:41:08 +0900 Subject: [PATCH 073/147] removing unused variable. --- elisp/ghc-check.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index d02142a..4b85773 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -136,7 +136,7 @@ nil do not display errors/warnings. (defun ghc-to-info (errs) ;; [^\t] to include \n. (let ((regex "^\\([^\n]*\\):\\([0-9]+\\):\\([0-9]+\\): *\\([^\t]+\\)") - info infos) + infos) (dolist (err errs (nreverse infos)) (when (string-match regex err) (let* ((file (expand-file-name (match-string 1 err) ghc-process-root)) ;; for Windows From 8fa3a1e3cf1569e3f1a7e15b3c0ad973a9c1f8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Thu, 20 Aug 2015 03:46:20 +0200 Subject: [PATCH 074/147] Add missing copyright notice for NotCPP to srcdist --- ghc-mod.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 010bd94..500a235 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -32,6 +32,7 @@ Data-Files: LICENSE COPYING.BSD3 COPYING.AGPL3 Extra-Source-Files: ChangeLog SetupCompat.hs NotCPP/*.hs + NotCPP/COPYING test/data/annotations/*.hs test/data/broken-cabal/*.cabal test/data/broken-cabal/cabal.sandbox.config.in From 26d72b0b88266ad6fe87bc96a827eb568ac84f83 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 20 Aug 2015 11:33:55 +0900 Subject: [PATCH 075/147] supporting map-file in Emacs frontend. --- elisp/ghc-process.el | 48 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 3e0c4a5..47ceb24 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -16,6 +16,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar ghc-process-running nil) +(defvar ghc-process-file-mapping nil) (defvar-local ghc-process-process-name nil) (defvar-local ghc-process-original-buffer nil) @@ -48,15 +49,38 @@ (ghc-with-current-buffer buf (setq ghc-process-original-buffer cbuf) (setq ghc-process-original-file file) - (setq ghc-process-callback callback) (setq ghc-process-hook hook2) (setq ghc-process-root root) - (erase-buffer) - (let ((pro (ghc-get-process cpro name buf))) - (process-send-string pro cmd) + (let ((pro (ghc-get-process cpro name buf)) + (map-cmd (format "map-file %s\n" file))) + ;; map-file + (setq ghc-process-file-mapping t) + (setq ghc-process-callback nil) + (erase-buffer) + (when ghc-debug + (ghc-with-debug-buffer + (insert (format "%% %s" map-cmd)) + (insert "CONTENTS + EOT\n"))) + (process-send-string pro map-cmd) + (with-current-buffer cbuf + (save-restriction + (widen) + (process-send-region pro (point-min) (point-max)))) + (process-send-string pro "\004\n") + (condition-case nil + (let ((inhibit-quit nil)) + (while ghc-process-file-mapping + (accept-process-output pro 0.1 nil t))) + (quit + (setq ghc-process-running nil) + (setq ghc-process-file-mapping nil))) + ;; command + (setq ghc-process-callback callback) + (erase-buffer) (when ghc-debug (ghc-with-debug-buffer (insert (format "%% %s" cmd)))) + (process-send-string pro cmd) pro)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -71,11 +95,12 @@ (t cpro))) (defun ghc-start-process (name buf) - (let* ((opts (append ghc-debug-options + (let* ((process-connection-type nil) ;; using PIPE due to ^D + (opts (append ghc-debug-options '("-b" "\n" "-l" "--line-prefix=O: ,E: ") (ghc-make-ghc-options) '("legacy-interactive"))) - (pro (apply 'start-file-process name buf ghc-command opts))) + (pro (apply 'start-process name buf ghc-command opts))) (set-process-filter pro 'ghc-process-filter) (set-process-sentinel pro 'ghc-process-sentinel) (set-process-query-on-exit-flag pro nil) @@ -133,10 +158,13 @@ (forward-line -1) (cond ((looking-at "^OK$") - (if ghc-process-hook (funcall ghc-process-hook)) - (goto-char (point-min)) - (funcall ghc-process-callback 'ok) - (setq ghc-process-running nil)) + (delete-region (point) (point-max)) + (setq ghc-process-file-mapping nil) + (when ghc-process-callback + (if ghc-process-hook (funcall ghc-process-hook)) + (goto-char (point-min)) + (funcall ghc-process-callback 'ok) + (setq ghc-process-running nil))) ((looking-at "^NG ") (funcall ghc-process-callback 'ng) (setq ghc-process-running nil))))))) From 19e62b0a6a8be66268c01a0b29c80384e23014bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Thu, 20 Aug 2015 08:43:36 +0200 Subject: [PATCH 076/147] Inhibit stack support when dist/setup-config exists --- Language/Haskell/GhcMod/Cradle.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index c68ce21..322b82e 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -80,6 +80,12 @@ stackCradle wdir = do let cabalDir = takeDirectory cabalFile _stackConfigFile <- MaybeT $ findStackConfigFile cabalDir + + -- If dist/setup-config already exists the user probably wants to use cabal + -- rather than stack, or maybe that's just me ;) + mCabalSetupCfg <- mightExist $ setupConfigPath "dist" + when (isJust mCabalSetupCfg) $ mzero + distDir <- MaybeT $ getStackDistDir cabalDir return Cradle { From 86c35733cb1f293443048dc28cd8b81bb39fdbf4 Mon Sep 17 00:00:00 2001 From: Pierre Radermecker Date: Thu, 20 Aug 2015 12:14:01 +0200 Subject: [PATCH 077/147] Update stack to lts-3.1 resolver --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index efce96e..e25c8d2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,4 +3,4 @@ packages: - '.' extra-deps: - cabal-helper-0.5.1.0 -resolver: lts-3.0 +resolver: lts-3.1 From 473f1e09c77ee3bb1ca25e7741994b0f33a9106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 21 Aug 2015 04:12:53 +0200 Subject: [PATCH 078/147] Rename withContext -> withInteractiveContext --- Language/Haskell/GhcMod/Gap.hs | 6 +++--- Language/Haskell/GhcMod/Info.hs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 198c3a4..cbf773b 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -7,7 +7,7 @@ module Language.Haskell.GhcMod.Gap ( , setLogAction , getSrcSpan , getSrcFile - , withContext + , withInteractiveContext , fOptions , toStringBuffer , showSeverityCaption @@ -214,8 +214,8 @@ fileModSummary file' = do (Just file==) <$> canonicalizePath `traverse` ml_hs_file (ms_location m) return ms -withContext :: GhcMonad m => m a -> m a -withContext action = gbracket setup teardown body +withInteractiveContext :: GhcMonad m => m a -> m a +withInteractiveContext action = gbracket setup teardown body where setup = getContext teardown = setCtx diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index f22451d..e952838 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -35,7 +35,7 @@ info :: IOish m info file expr = ghandle handler $ runGmlT' [Left file] deferErrors $ - withContext $ + withInteractiveContext $ convert <$> options <*> body where handler (SomeException ex) = do @@ -61,7 +61,7 @@ types :: IOish m types file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ - withContext $ do + withInteractiveContext $ do crdl <- cradle modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) srcSpanTypes <- getSrcSpanType modSum lineNo colNo From c5db06af0fea8044757b7b563c0e7ef9de2fe6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 21 Aug 2015 04:21:27 +0200 Subject: [PATCH 079/147] Fix missing liftIO --- Language/Haskell/GhcMod/Cradle.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 322b82e..022bff2 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -83,7 +83,7 @@ stackCradle wdir = do -- If dist/setup-config already exists the user probably wants to use cabal -- rather than stack, or maybe that's just me ;) - mCabalSetupCfg <- mightExist $ setupConfigPath "dist" + mCabalSetupCfg <- liftIO $ mightExist $ setupConfigPath "dist" when (isJust mCabalSetupCfg) $ mzero distDir <- MaybeT $ getStackDistDir cabalDir From 85722ab6f2bdb68c51953cce2f911f01875a0933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 24 Aug 2015 15:11:05 +0200 Subject: [PATCH 080/147] Cleanup --- Language/Haskell/GhcMod/Cradle.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 022bff2..b348b89 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -83,8 +83,7 @@ stackCradle wdir = do -- If dist/setup-config already exists the user probably wants to use cabal -- rather than stack, or maybe that's just me ;) - mCabalSetupCfg <- liftIO $ mightExist $ setupConfigPath "dist" - when (isJust mCabalSetupCfg) $ mzero + whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero distDir <- MaybeT $ getStackDistDir cabalDir From 7e908fcd5e58d7291313803b4d5f9caa6981a49f Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 27 Aug 2015 10:42:28 +0900 Subject: [PATCH 081/147] Using "^import +". (#570) --- elisp/ghc-command.el | 6 +++--- elisp/ghc-comp.el | 2 +- elisp/ghc-ins-mod.el | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/elisp/ghc-command.el b/elisp/ghc-command.el index 7970a12..df9a69f 100644 --- a/elisp/ghc-command.el +++ b/elisp/ghc-command.el @@ -53,7 +53,7 @@ (let ((inhibit-field-text-motion t)) (sort-subr nil 'forward-line 'end-of-line (lambda () - (re-search-forward "^import\\( *qualified\\)? *" nil t) + (re-search-forward "^import +\\(qualified\\)? *" nil t) nil) 'end-of-line)) (ghc-merge-lines)))) @@ -64,7 +64,7 @@ (while (not (eolp)) ;; qualified modlues are not merged at this moment. ;; fixme if it is improper. - (if (looking-at "^import *\\([A-Z][^ \n]+\\) *(\\(.*\\))$") + (if (looking-at "^import +\\([A-Z][^ \n]+\\) *(\\(.*\\))$") (let ((mod (match-string-no-properties 1)) (syms (match-string-no-properties 2)) (beg (point))) @@ -73,7 +73,7 @@ (forward-line))))) (defun ghc-merge-line (beg mod syms) - (let ((regex (concat "^import *" (regexp-quote mod) " *(\\(.*\\))$")) + (let ((regex (concat "^import +" (regexp-quote mod) " *(\\(.*\\))$")) duplicated) (while (looking-at regex) (setq duplicated t) diff --git a/elisp/ghc-comp.el b/elisp/ghc-comp.el index 2209ca2..20be9a1 100644 --- a/elisp/ghc-comp.el +++ b/elisp/ghc-comp.el @@ -265,7 +265,7 @@ unloaded modules are loaded") (let (ret) (save-excursion (goto-char (point-min)) - (while (re-search-forward "^import\\( *qualified\\)? +\\([^\n ]+\\)" nil t) + (while (re-search-forward "^import +\\(qualified\\)? *\\([^\n ]+\\)" nil t) (ghc-add ret (match-string-no-properties 2)) (forward-line))) ret)) diff --git a/elisp/ghc-ins-mod.el b/elisp/ghc-ins-mod.el index 14db867..d567ec2 100644 --- a/elisp/ghc-ins-mod.el +++ b/elisp/ghc-ins-mod.el @@ -56,7 +56,7 @@ (defun ghc-goto-module-position () (goto-char (point-max)) - (if (re-search-backward "^import" nil t) + (if (re-search-backward "^import +" nil t) (ghc-goto-empty-line) (if (not (re-search-backward "^module" nil t)) (goto-char (point-min)) From 8a2e490592d24c91198170b7ad047f0a24806f6f Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 28 Aug 2015 10:23:57 +0900 Subject: [PATCH 082/147] ghc-debug now displays the output of "ghc-mod debug". (#571) --- elisp/ghc.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/elisp/ghc.el b/elisp/ghc.el index bdad76c..0c4f967 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -138,7 +138,8 @@ (el-ver ghc-version) (ghc-ver (ghc-run-ghc-mod '("--version") "ghc")) (ghc-mod-ver (ghc-run-ghc-mod '("version"))) - (path (getenv "PATH"))) + (path (getenv "PATH")) + (debug (ghc-run-ghc-mod '("debug")))) ;; before switching buffers. (switch-to-buffer (get-buffer-create "**GHC Debug**")) (erase-buffer) (insert "Path: check if you are using intended programs.\n") @@ -150,7 +151,10 @@ (insert (format "\t %s\n" ghc-mod-ver)) (insert (format "\t%s\n" ghc-ver)) (insert "\nEnvironment variables:\n") - (insert (format "\tPATH=%s\n" path)))) + (insert (format "\tPATH=%s\n" path)) + (insert "\nThe result of \"ghc-mod debug\":\n") + (insert debug) + (goto-char (point-min)))) (defun ghc-insert-template-or-signature (&optional flag) (interactive "P") From e2dadbbba0eb1fb401d8334a50d311037a6b346a Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Fri, 28 Aug 2015 14:16:14 +0200 Subject: [PATCH 083/147] README.md: update the instructions for Nix & NixOS The stackoverflow article the README used to link to is outdated and no longer applies. --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0752888..28b1b3e 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ package is called `ghc` there, not `ghc-mod`) and install the ### Nix & NixOS -The installation is a little more involved in this environment as Nix needs some -ugly hacks to get packages using the GHC API to work, please refer to this -stackoverflow answer: - -http://stackoverflow.com/a/24228830 +`ghc-mod` works fine for users of Nix who follow a recent version of the +package database such as the `nixos-15.09` or `nixos-unstable` channel. Just +include the package `ghc-mod` into your `ghcWithPackages` environment like any +other library. The [Nixpkgs Haskell User's +Guide](http://hydra.nixos.org/job/nixpkgs/trunk/manual/latest/download-by-type/doc/manual#users-guide-to-the-haskell-infrastructure) +covers this subject in gret detail. ## Using the development version From 70c2e0c589430be2d4134d6d8ef26c86e9529674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 31 Aug 2015 03:39:47 +0200 Subject: [PATCH 084/147] Add IIJLAB presentation --- doc/presentation/Rokkitt.otf | Bin 0 -> 44508 bytes doc/presentation/SIL Open Font License.txt | 44 +++++ doc/presentation/architecture.pdf | Bin 0 -> 5746 bytes doc/presentation/architecture.tex | 44 +++++ doc/presentation/auto/main.el | 14 ++ doc/presentation/current-architecture.dia | Bin 0 -> 1701 bytes doc/presentation/current-architecture.png | Bin 0 -> 18358 bytes doc/presentation/gh-stars.png | Bin 0 -> 10524 bytes doc/presentation/hackage-dls.png | Bin 0 -> 11916 bytes doc/presentation/logo.pdf | Bin 0 -> 2067 bytes doc/presentation/main.pdf | Bin 0 -> 121827 bytes doc/presentation/main.tex | 204 +++++++++++++++++++++ doc/presentation/planned-architecture.png | Bin 0 -> 20532 bytes 13 files changed, 306 insertions(+) create mode 100644 doc/presentation/Rokkitt.otf create mode 100644 doc/presentation/SIL Open Font License.txt create mode 100644 doc/presentation/architecture.pdf create mode 100644 doc/presentation/architecture.tex create mode 100644 doc/presentation/auto/main.el create mode 100644 doc/presentation/current-architecture.dia create mode 100644 doc/presentation/current-architecture.png create mode 100644 doc/presentation/gh-stars.png create mode 100644 doc/presentation/hackage-dls.png create mode 100644 doc/presentation/logo.pdf create mode 100644 doc/presentation/main.pdf create mode 100644 doc/presentation/main.tex create mode 100644 doc/presentation/planned-architecture.png diff --git a/doc/presentation/Rokkitt.otf b/doc/presentation/Rokkitt.otf new file mode 100644 index 0000000000000000000000000000000000000000..64a20c00bfc0447ff9e38c057a669f4450983fe9 GIT binary patch literal 44508 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 p<}UO1e=*LP0=4Vi+lj0i>i+B!(E2 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 literal 0 HcmV?d00001 diff --git a/doc/presentation/architecture.tex b/doc/presentation/architecture.tex new file mode 100644 index 0000000..ef85434 --- /dev/null +++ b/doc/presentation/architecture.tex @@ -0,0 +1,44 @@ +\documentclass{article} +\usepackage{polyglossia} +\usepackage{xcolor} +\usepackage{fontspec} +\usepackage{tikz} + +\begin{document} + + % \begin{tikzpicture} + % \draw (-1,0) -- (1,0); + % \draw (0,-1) -- (0,1); + + % \draw (-0.5,-0.5) rectangle (-1,-1); + % \end{tikzpicture}. + + \begin{tikzpicture}[every node/.style={draw}] + \matrix [draw=red,column sep=1cm] + { + \node {8}; & \node{1}; & \node {6}; \\ + \node {3}; & \node{5}; & \node {7}; \\ + \node {4}; & \node{9}; & \node {2}; \\ + }; + \end{tikzpicture} + + \begin{tikzpicture} + \matrix[draw=black,nodes=draw,column sep=1mm] at (0, 0) { + \node {check}; & + \node {type}; & + \node {browse}; & + \node {find}; & + \node {refine}; \\ + }; + + \matrix[draw=black,nodes=draw,column sep=1mm] at (0, 0) { + \node {check}; & + \node {type}; & + \node {browse}; & + \node {find}; & + \node {refine}; \\ + }; + \end{tikzpicture} + + +\end{document} \ No newline at end of file diff --git a/doc/presentation/auto/main.el b/doc/presentation/auto/main.el new file mode 100644 index 0000000..7544b2d --- /dev/null +++ b/doc/presentation/auto/main.el @@ -0,0 +1,14 @@ +(TeX-add-style-hook + "main" + (lambda () + (TeX-run-style-hooks + "latex2e" + "beamer" + "beamer10" + "polyglossia" + "xcolor" + "fontspec") + (TeX-add-symbols + "gm" + "gms"))) + diff --git a/doc/presentation/current-architecture.dia b/doc/presentation/current-architecture.dia new file mode 100644 index 0000000000000000000000000000000000000000..12f369239f167fc93397474036e3dc772c2f757e GIT binary patch literal 1701 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 E7kybrMgRZ+ literal 0 HcmV?d00001 diff --git a/doc/presentation/gh-stars.png b/doc/presentation/gh-stars.png new file mode 100644 index 0000000000000000000000000000000000000000..51ca361ad275bbb5b37b3fe3bdc0d61d91571f6c GIT binary patch literal 10524 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
ft{#scx@$O!shSrDHKXr^G#DT~q|D z_H=^mhLQkeBtMFA$-E1!>l;&AQYXb>?V+IKl-S5eLYTX zTl^?a^Jc+1lfzPD~wuBI;^zcafD;WhSsYJ>mk>KaLzx;H!KW}-q$SE9v@_niCXrRV>RN=T0R$*dg*V- z7*WG!`ddS%wRB;&Db^a55; zsUQb3e*5@W2REpN(QWsS&r|DCR%mI=t))SsWRjY0Jo{QhzB-h^OEw>)ZHz}97M_cE zHr_e0y2ewVxvpHg914?u_4MtAC?xf@td7VxHD}k#uRGbPUj-kWJn!_%QpxRL( z7BFZ=x$-Ovy)uWbU;1V0j5EU2ri8=^pzYIfuQkNc&uq z4x^isb5Ox^If6ABqbJRoJ1VVyw2`%_Z)$3SpixCpfQ{pbC0= zqoONtV_hFEKL_2F{46yi;J%R)@whpNJmHh82X^m&5}TWmH@iJ4+WUco;Gk0Pz|&o8 z8KkOasmfQA8}dz+KHS(#B~0u`(uns_)jiIuBJqygbz-NOWs~ROoDb~ z#KA^?SV?oW++8?COT$iBZ#pJ-wq|*zjT8a+aPnM{rkEW6=|oy#4i#ba5*}zznf^+4 zDftF2j9K^A|6NXcf}Q$KZ-E6%C{7KV_!ma5$6FN ztKm7J9Qs=%cWNB9uB;w$SBHTgp6$voI?KhAQcHpe+IP5s;OIlv*DF>;7dx}=$}9HB z&_-s{gSzdoCL6OMyCoK5Jg*}Bva4m4Rv8-i$3*=CvGzZ{Bw{`&-!p&A@c zv^crNvo-YDwWamoQdeX?vd9Vh4Y`zD5>z2CEGbFWYFziuG>wPu1Nojuj6ilqCVlppv=#@Qqz2TwWW zLZ>+`jy`^ct%_)KlY8~80fVxlf9Q7xubeWCY{Yqu96&jhp-tC>@+TinGcypKQaEMB z6LD=T`veu0Qd2|vrD~;$`4uYqhjJNel>JcbeW`zVvmG_WgB^>AWAkxzEm!|Tz2yw; zG9e4xagBsxXg{roikqek4wHh0CZ0Qyp`p7;U3KkeNC{MpZfq{-1+Y6rPpzQ+p8>po zV|D+9>irKK-~S)L`+sqK6#@ST+V`LI-oFUJ|AYXAf^YqEbd}0vCS?x)!5(EK6vfLw H8U_9@-)KQp literal 0 HcmV?d00001 diff --git a/doc/presentation/hackage-dls.png b/doc/presentation/hackage-dls.png new file mode 100644 index 0000000000000000000000000000000000000000..cc190f72542a1225807b030065c8e7c32feaa87c GIT binary patch literal 11916 zcmb_?WmJ^iySE4;h$zybfOJZWv?3r4(w)*Ejr1T2NQrc(bhm_bN-EumG!G!%1LxxT zpY!2e>wJ69ER^L;ySgJ{7y%C z{Je`A=L_0nDyqbMABI2GK^`iMf947-i@pw(jPmEsYrWGh8bNU$5!%qxU^S}p(>f;q z{T$5%lS)*SGCY2s;`oriY1L!9UrzEZRmUnaHX_o|cED@9Hq~Q*%hH0I^nouW#=k#2 zkFXxzM0VKMZH+US6)v74>V&oS0^!64^wWYrdI6-tE&yQnSG>59J)D;vIw6(Qw z-@c7NT*2SA=bM^xb0x2Sx$dv@XUip`-zT4`v0vnO-jJk;@W&>*N6c=p*coa&_x*(x z2bpCRKQ}iwGqVg0?&RcTM|=A-KE7Rq$#k`iQeLGr^XF{#j**d(<=)ui&B@`mwzjb` zHL7q|SJ#8JU&cQoXr-j2&cb{Yk(8ru+d_!Tix3j7sY;8*r6n4|U=vQwHh=7wqN3g> z3jre|DoHi@Wb*Rz92^`jE-qBz49v{T+}sDcZ^bQTeNb`?EiCxVf2K`LOaukp!8TfZ zHr|~YpPYBV>cRa`Y`?p5&l+W&p zzj>3X!ZbP-rouE^YWQInb*|CNqhhjY@-yv;myk&h{?Ni)gU2Zu5kCH13gH=_b#V=4 z78aHyZi|SltgJCreym^)<4$~h{GHTjMP%G*#HbG!$11k-LaU!?f5L9jx|T)=xez}a zTS6iGG1r#B8Wx~R6P}yHzp<~Q{-TzA7^4=;*{1KYaKQF7GgIo=02>y*B2IFU-$*9K~qKov=MyA6dLHR#a~}n%@;h zQJ|9l=g%K_Qli&DI%OGf?-3I-v)t5u=_^Z*m&yFjcP^L4inRaEH;Hg@Ra920Jc-qB z^yGt2-#$e{LyL-zZkP_l$`TQmm;c<*Ahge=tD}Q|=a%%<6>s}&{kyNt{~ninx%;8z zifB!JJ(hl`}Pcyn*v9hA#4b?^X`=rfha-lo3LReT>*aE8^!9b-zU60@J^;fJB7sX4`tj*fwWfy`6| z5oDC|SgMdm%=h6Su?*dMm;bCQvC3*ZLZ9E`Xp@u~nTPJ@=VxJIVK$heTWzfg0a4|4 z)z#UV(fRKx>vne*UXqcKt%z>U)G9as~8WHt>Li#A4z#6b}Xdva&L_y(LVu zit_Sum#wMr7zn^O-%c;j|4Mxr6h4=oevl)dES(@OCPwTsrC5}3hMTyrAv&p`rInz; zLRVr5$uy-UdMaSt83M6FYDelDmm%?$gx`r_-E3>BYAT@!f}fj=^UbIAvlkGBwz;g@ z)oT?=CBMuY&o-EcIH4@e%0uK%M#aQLsB)EQB&DQW>PaGkYHFV8>+8!S+9M)xlLLBt zdZy}}|M~Lx<;|NnlaiA=jO-iS4=<3&^P?@Y2M?}SI$%57;1SC|6DD%mJ&F5v^g*$1 zeYT^KiR;m3yu?>2dHL+id&wl6oScMDbS8>*1)z$SX>6{njD39-mYbWKkwImc)nsVg zA@$-#3xs%Q7zG6m&hMqAH#Rn`;gU16vmVFW4NmJrP~L>j8JU>{VB<;++b%9HGL`aV z>98;`hw%jCk+#8!d30j3ON=iz&lR~ST0Rb;Qpzr<-hxVg2O*R!oz+Ct4@AlRf zYqT_UCm|sr0hi4hgBIW3ST-z=LO5$EYxOd*Pl}6+aRb|Xdh#Jj`xALnxh;lv7rWfJ zgM0=()@u6Ll;fT{Z;bFct$k_9dHe~|mXw#5cSym(p}g9Zy|TH#zuyT-%=jy=U3ybJ zcx`PBdXu}m`xI38sVR3iH*B(}o895m68w+W5u+g-ul*w;B8GDm5U`SDfp;#>&e76Y zF)=aFE6B;oAx$CfkDw&w3euv_D<*kf>~<{$!=^1+xUCL68XD^8SWcGJu_hOEWrsxY z@wpK%OBXGTytqKeCUyGLj>&nFU7t{*-&9>)O{a8z_|N!RQmJTqu5SHPO&Z^M&SqkU z?txlKngkxJv2z9_52>8Jy?v86azbCH!W4V(8|=Yh2a*1lXmXp6{{D0{G^MwU;4lm; z7TN+F$(Ivt8x4RF}dwXT3U)JR> zTK8$Z&K=zl4<@t_FpDt!J**(2$`pN}3(ez2j%CpA-@m_KKLnA8b?6r-$`tHP9L&sE z;@=Hfw5vl%B&@9dIY0i3#V`wDE9r6(mvWlZyS8TDc5kFen^%aUAv!u5HZ5}(qL{%( zPS|q1_|esi1@uh8XApn9~s%ja=a7?|0Vno9BD50oiERzo$T zGL1exeD=0>@iOyYR5pP*H&IcQm6T+?cwU7Nrz2L-{T~Uqd?|t6J0)ps(4^kXDDxz? zDQqb9^_9(3WqPAVled>m$>Mm4KAdl3N(-t{nd^L`*To>8t*z|<>aAP1$^7Q^_+ zcr8aTo>E%{L5Wnk-&a{<+E04PRid0T03a*mq1j?r(3jNIK|ac2jsS&~pyc!{GtV$OZ<-n~o;7dtG^Z;$)maK^8n zaVXgps4(rlhN?Vb>0F>zbYf$?^ScFZ;2XC#lqFMuUs&U|Z>lVbF#WLykw4{$kEv)2 zp%QyGB8W$X_O{YedpUFL1g$9naj|>*ZrIprrN`lhc;nuJHhm73=3r*T* zTr$<0e;wQ}^y{1glQk60*4PRDmcO0l&zF1q_K7oPmJ%klX&Tk)j7mmAcrWI-7BRoe zrt9H`Y6@E4$NM4|UmVp{2JH#o6k@OoD^XO+{bN~GXTR8acEAqt=;dvprltn%{6M4( z@|WD!;G<&$J3IRql7^$L>Dbs zoszwso!=pI!>bxh(RYfSpwhkhJJ;~z$B#RH8JU?htgJSJDMH@f-u%=MOR11G)Z@Pk z-2c>0QX=!&O-YzIo_TPBNtL)z+YZozT@`?z!JSeN)m@Lx|E!$g@u$&O5$WN z^6c=RB|GT)S;9Q~?ao_MJSY1e2W$4wG@#$WMhJTxSp(vRy5097<>Qz5KbC4LD$TNu z_sIo+i){7v$(Gx1EH1X0v%d&=Z+CHGKcp%t*?rE8`enFx ziEcf7Pny_XxC_72TJ!gSwSAe@{=_bClRAgxAf-GO28L&@JCq((HypdwFjp7{W-xvp zcAVsp%Enw{wv-s-uFqK+<{?p+mQ@K&yMW!&9DS>QNawiJIxOF@n5lnfi)W#{=J#iN zdw@?2N;7+HtGd3I$wZm==`s;9M$5V}KvIM3(^T)XANWK1SxVvk3}qUCQLWL}**-BU zaV>nC_bw_N!PO;_ zT`PFL`+&qOy;FMn$V5d2f7JBiWS3u|@%#6rEgvz~JWjJ)MEfTQ#_0|b4vq&;Cm>Ho z(_V#gkcj<=upBE$J{6`}xm!3Bes8VQ=j0N)r__HewlN#du28?H8ECNh>PDkF!^K81>cb_ z97&G6WA7Cug^4V?_lQ!unlr3~L~E zCT_TDLuoLIBeU@^LJbFk)sd3gXbBPe* zUuDK!P!6G$y?XTuI%Q+z;^Janh2`9LzEN~K`NuCFC9Q{Wa&hrBL{G3zj&?J?o7>#9 z&QpO@9|mIZfLg-ybUF6o#S(^22DL))6Wuz+-lr16qXrS}McUu?c>VRTfnNV?if^a_ zILA0359`Tja_sJl=aO0k@YEb;@SY}_iAh9+LaUy!lF`X@l%(NyugD`I_mLZq1Of2A zovk}PJDZxE+{GXCE#rs!1LX~HB=_N2S)gqAV>&v2mP;v`2*4KfbaaG7M8M~A6jCn_ zh85a}SXf!zzPI^pyn4PaVmZQKliaNPZh(cmGc}NsPTJ@T2q`V2v9hcHNlx1W?t=<} z`qIG5(^K!eYs*{ffj}&}R#FZlGzFQOV#~S$5?owS;pJLpJUl$93c#CptnN;wu$fAx zYI$Xe$YM);->7OB+O^GQZ>|&E)DU7K%7HVjazpdlWz%oGfNr;2bFS=x5k>UK<2wDH zCF`#aB=_Fy<}yg~d&Df{w!eZsdW{JN_3pIA>U-xEwL$kHp+h>mb5Oa9GSNz9DOKBk z2v^|wleoRHk+tx65|Da-PL*sncMjhtA#Zn zsXK=^P!;$JaGq09QMoRa`$%V1Rz}fJexmI1U3iY}v8#H>jm9BfLz=;5_9-Waeo+2% zDH4Mg>6Y*RLXI(9axOh{h4*#bq7_hpQMofmJ)_&V19o=oY)Fp!`0QJ1eD%Fg{c@Ip zLcZ}Yz?}UjJv)bTu4v>akFxW|`^V#gw+Jm856lAEw6wHZ(E%owM6?Qcp0JNzMk)wD zPMxcNr;zxrQR3?<@_0V|uZM+2o^+OPiPz8B)a2S!El#uh!B4|vk1}c?=TR0}HZ|6z z+}!xi%U)GsVq<4bT^vo@PVs*dQrZM5DL|J5>kiXZ;M1YLWFhsT;ibi~D8~G!Pqnt@7M<<_@ zyVY;l`Vn&kwPOT{;qbyOgECEw>tqPaBv%mWc{FLZ6JPFm>VUe(o}hdNjIgV{eaGD0 zQC_9?Y*KE@?ZYHST2Lc?1ciP3#yhi2*iKAoS5OV?`)|pCIs^Vi zKG90aPkJSR$B*AzQVd^)+= zJNPGGn?v;jmqORv!a^2CMn)zkCI*J?{{B%nqs$bH_w?E%vQjNvq@`*11>W${(9lp( zi6)LSW?*M2N%dGCZj8#9W#{D|DH!oVpeaa*`W6+j@{NH;#)ryZQBh%-U;x$RNEyf7 z>g4>~(LIg)nO$>F&zherIi+f#_t5Xbce(vLFNtzG0@6Uls2`PTaB4xUwilB=zo-8-nq^UMd zsrg-OAt8aT(0g#;#HAw_DuD-B1rN2q&?Oul;(};~n6k8;oZ!)!x71Zs6#hm%?7*U? z)q~v?_OXk<#Z)=kkC(Tv0Cz-&8K9v{`9HsN|Mh8H+907BDa%o*xr|ZwlbZ%{2SlEQ z-8wi#M7&mGg&-5u)z$5dAN>*JShMPG-rwjuqWtqP6t<((%ljE`{c2zQSJvMs>2XhO zNbPMGoyd4r^3ZLM;0)LIqubltKmPlG9oy&b?e6x$JAn2j%~SL4FZoQPql1Iix3{{$ zp+G*>)0_L-bR}%}`+529EytB+`TWGTu*k?rVP1SxmIkr@vNQeRjEoFGg#S3s$E%#R zor(?eCwHd$D`v;V#@<$LsIr-M0r;4gmj_@vH@D}w2qqMj2HdA;H0dS0OjA%kKO-U{%E`{|?C4iHrABkWFg43pbL8?7dYH>{~oG%UCi zf{ECw&3}oii08_uZ49{?ZB`7KUb7jaQl<%(z-`jnBMB=r^NU3#F@XgWjaWA zR5jHqAvswt!r;61`h9x-HcpQOm89J`BNG!^*&;%6@?x6>oU@cFi(&t|M$tp%w#+6) zy8K_}4h{~jt*yV+?n6Bi2Xs>%(Y(^P1EA6WKE*x9kBZW5NqDiBk+{oD)h|^=EF>PtGv`nV$c~%ynIIo`e12XsR2nCFM#iUhEx0Oc@23*iuRIydywUq| z<6#|e9p%8Au5nyu_clCIR1I;5xFMoiQs+Sl$P^AvOe7PStOy4m;6oBYbX4Td`QN6t zQq~@3caS8Zk^%QHD#2oJem)37*5&?t895Xbl4akF0%pdj-l9U7Q>N@-j)8u z4<9}V2?`b#6>V*9YE@ZQ*)53v!Lr|5lCp8jJv-XM8b<7<-j{WsF3}fJHdn5}F5U9Ygg zCggtLbJbwKDBnKu*T{c5^uA zzH)QK=~tz^5jUg8#>Pz|L7UJ?HkvbV#JH^|Neh)cB(<>K91s=p7+9orimU1cA517Yc!zdxgxrNoIbsvy^DO=oFl#%uql9jHgMT`)+fSeL=z zquhJ=tP~*oEiA&);WXvu6RnMG_SN=Vy7z%ZxM|VR(#G#w%Exp1T_i?D3j*)vDkUT& zWDm<(4?$URS~$+fiP@mKhU1`fPsEe~P}*Q)e}$sSxwL)?5MxAtqVadsK@(tQKP;l| z#OuIF7h&QW>9huINu<8NhTl*h7C=os2IJHS39D1m83fYQVLdpqH~) z+Y=Q~#7F<6rm&f<$9A^PWT0TO&efq{YPq|f+&+I)F*sbJPhZu5MNy*=`ZK6Eq`-36~Ac8wPAh`s%M12YkrPcq{ z^zpGQYll9u(tcA`UhETHX|Fm+`D@}s%AKoOVJg_jA#!;_z5cP{28C>n>-mw54YIii zl}Syb+5Wm<{+{MR*p{xn0`{aDc1!)UTf|4ugq4(W1olIA2dOpsW9bQ^eN0)R7+VYM zK$gGs{@uvDT+O5+!t-O{6%O7kGz}n14d1`x;^I1`+`X%kby0omfi6fqw8@_P{k#{U zKct^lXk8W4J<<3YEAoLWx}nPn?;eWU5cto62Z7JLFP_ymD0Zf8J*0zFYx6?F-a_&N zCFHf8o5@k2u$fi`WiJWYCawLZHkkDxh~2V%3UBz$QeOt#8?F9S4g!gbhX#Q!)9%iw zy1pvjI@_j3*$01PW8*w;gE|R}=!eH!)8FjEQdle$8wdyq%Ljw@V_hJ;E!n}^IWliP zdFr2*Mme$#RjEL|gib&3)2E;N+R!cO=J$?|SAQgdI*YoOZE0xOQap}@I@{XT#wTzc zlA1dJ*mJ&u^QkQ*0$6z5Mc8it7#xJoFs{2rzgD&p*S$rz}3 zZ4HkAprfuifog-BvjdQlTz=PxE_F4v)~T;DF)VKCctkquvC>&nDgv@JHgyG3>52?A zxR~}-RPnl`+~&>kkL4JQeVUarGhJ2s!!K1kdU}HB2j9JTfikmTva`E8*@U=)4FI4E zbiE7^=fKmcAZamiaSH*G0h2w*By%eS5Q7bBnWv!(!iGG7=Nq_s@aFSuWeC(ov>C(HdR?VX*M zb%fk5FZjOCPabCrclly{Nl#DTLI};$1N6Gb2>ohnl0n>ZpxU3gM+BmUc}wJB1SB0H z*7f-jpJRzCsJo?7pdGkuJq31Z7i_&bQMSp^*WC?C4!znZ{<$3Q!gCsRm|#a6|9x#0r2I)^_xdLzp!r*@ z`^l0Bl&+wj;;c~c|4mFyl{9&Zii*w;*7twCqG%zD)*X(;8%RdWzxNzC|IFUlJof7% zwXZI`O~4KM&#Y4%?htiOKzf|Qzy^4H0Hi^x^MX{id--E{n2VP;c7GiMTY@S2iB{#> zudlC|qM4|vjbQ@CE<6u1k||nxnz5s^vvmrAM<-hYim)hzAwDp%hdh?}-?8F2Dg99t zYM?;8`#a}#epICMB;YSA;9hnIDZ0oao!ULPr)8B^o{F&R&Tjy|0iT<$ZB$Pm0AWs( zxPf9&)$8B=)dMmHE-!eGglu|!?G-(5%3;8wHLC_J{ne$VW~m`pWR6nar-}>>mX%tK zn+au#*^)MWV2~d+zegQR6;aPqv7K-1@9Pt^|05 zpOH~YMrOJRA)>0Ql4$j1lJ1E#A8`YiiKytLhG>jOzB2+9G6ru0bC#ykD>g6mmAJfPilL{cV0|@_IB2QTGBOKq zx|)wPr4wU5dBK8_Q>kPVEC$fv4pdW!{fugWGo{@G0JTZ#*KjJN5HX8*xo zSsCyFBe?<{XGc&_5QuQ=2vRURS9A~r`KhGTNI%YBpq%6`9cVr?Uga6vUl|R6(6HbB z=*Vf>N1Re%oe~&4wKAH0J%DnHNM~Z&TvYBEeu(4yl*icrYVLo&TC#nAHPmF%8v|^) zc<9YFdA1nNE^~fNU)WguPcXg}b|$n-KQXWJDwKVAvH+!2pz z>U&5Jm~DxJtTWPZZ|TQHbT>C?KeU6qOreoI1q}^ivje_F@OxqM%SM;SW-2Q4^HXBb zezHi1pKdzTdcu1Id3_;FuPn|U7Nn*5^tj*MJvyR0+=Iu2%>7pBD+qJZT4QpcWQ=d; z8y}fIUv2N`fW@+|tml|YBGP4}YST|rQ|`93wA`l<=3apfgsg3;%Sak2(NE=)1>O-W z@fD=0_dmE9!G4Ns75>TdsX?A?WHj^Br%yipP~+OKv_p8&Z_a34`)wYNksU^5!ND>H z2EQR+#*4JyU0r&8xE~u6Qw`?_L>aUw0e=3V4kI(ORUaQ85U#D~8-)Y}v`ONC(Ljr=)EEyuOa3KlQ)yE-yVYcD^wqWT8FE1$`#rTKSp&G76E<0wZZ30|L%LV+YqwOAF8eFt=(@dKaxPpkl&}U>T!r zZC~LRP><fzyGx)LCx<12}%)sd$<0=uuW!wK^V@)j`Op&{y5pJh{puSx%tCr|oZpTejfL<=|- zCY+xWD^f`*A?05C!$@OyclYC${xEh0{_;2+3)B+2$kUUPyD)YL{;Z%MV}bkrMjLR0 zmBCaBeEg+_g~#EN?MC*%6QCS%fS&;40&5hq*8YLP3y^fMOsk>HK(La;sDR0>&o^C9 zId#15WdML$q{YSDwxj-c{xL7>Q50I@jZ* zZUKD>Ji%|QG%8H{llUC%WO2|IR$-)thUJ-{VA8N=H-4dJg}>|P>#Kquaq|2-h57{{ zw7$L`O2%s{y$?eS*_oMZcWFg&mW0fJpvw9`$HEE*&18RM6hvUFk=zH$?PEPXOtiFq z$tJ%tHGcbDPq$ufxw^T5bi{Er09s&`1H3c=#wzF{v8UJQu3t^z9V|0s8=!)|=dNG8 za}$R8^$6^jg!iK9JNx^+{rv=iVw~}tFj0DD_VgvrQUhp$8F0#AhT#N?0YN+dJ3mes z#L5B#f?!nN@e`%NMU$2J`Mq1AFb&hYho zc6N3!iEqEyN#x(j{C?AO(~}MgyLnAo+b*EWn~b8V+#)5CqBo!Waxj@M{c&!M-q8!h->C zZve#*7>WfF55<<8Jz(cxl#L$FFs0;VID^v2i$$5zXpa*&A8!A-B4WI)aS{dYrU-T= zG+mpysxhfs)|qPH@xd3zH>4(A?b$tbq<2dFE2`0uSp{1I$5rmCW2xqx=B}OT`SfY$ zvmy=ow#s<+xmEhiQZGbf)1*Q*8BgjDdGa->7cO*9B^$Ci;*(>lRs9XcpXK_A>TkcP z-T*7N_9<^0%D35Pm|INMKX@0?u|M;3n7L|b^YfydL-m>&Rs-|+Sbw3>WUE}&FDhK8 z46;_+R~B5i<Yb47Z!)fzm6`De2a0IBFF9F9Z(geYC~--e1K)yI z@JTt?YG8i)rlxy@W@NpmWXpA7e)`?W?{aKwBqvvOR%ZKW+ip@cyV5PQDoZLyy4IAo zM_LHmNV$3~Q*v3umpeYwg~3{&SbbZmC}A)0Il1%9xhq@hx|~Vv%T^aPJvcp9Z0XE6 zW0UJ6IaOX5@pi%;N2|Q26K2j^MXEfoiCbN;ueufpi^+lBGySl(&C2RdyF+x~e*M^j z`sTi>r_bIhePaDXY+-_8xPnbule^|Q( zCifK8=NRGnn28KxJZSp%usuJ++k81aiY3hG=!BCk<)v(gI@fHEgsjQm&W#??i5$6b z-xsQg=nJtdOJ@B#3Oz9RK251Vb}&~$8K1RvvK!pjou+Vj!k6XiDGW}m+;gRM2lM`6 zgI}^zZA=|pY>&Th*xBYZ8gVRh;C`PBs0{U+Fk2Cxn$*Y|Sf6mpOFe6!Zc~AZTT2U1 z@6uJ)8^`Kpp3iJw-WgPw_~FJ1W31oQp(sc2M|w+w<&Rn6%VX@;P_u4`{*Vvk9Tlwf zWz;l6#dc<08Cd;(wA##i#(aI7qUu zf2?7;T-V~OB?lieelgj6i5s@8wR`R3vY9^akiL0d@p#W0wZn3a_kJ3^`wiK%hnH3u zSEbzAmt4Ab_ZFKfKz~$O<@?wxTSiLI?df?W62cd~qH0LqA-!ogjy`*QLcAIq-d^ID zek&x+bmAw@S;|ze9#g>%5Q|yyzK>*ka*dWM6f69IpuLSig>uqy)xB^XdB+Bw`wue> zueQiK0y{2R_qM5k$&nl5vj=ru9cM0noIW)AR*R`<9cQ0ssK1Hf{|h|YR=(2n4ShPa zG$rM|Wk)QIo&Dm%T5_(NaF^yvUwqOB7A=UycQG5hlI|Eyf8U(Ala`{-TW6nX)p4!u z)PdI+sG>3YN$-wp_gD=-HP(a%#pm%RMjOVQLT)8!j;n;s6~C37m|f~^JluQuO5pEa zx#(rjQMKsKUfJ)Vs@0&>>>L1#c`xh-zaxx`a4X`9`xTW+qYwmQ>bl@-S|{^M!~#Mw z+{HjoPXfRP8KU45kj#D;q=IBFgr9M5n}0@~e?}oOm^3#kO-osr%rWsZe!PfCUx)~j zwlTF31^uQwEri9ljBj)=rfnuj-n7$VhbAQEQdEMIt+kPCAHd17Gw^LN~8!E zMnR%#oJBbiisyHSmpP$Yp@Bjkit8uR=796sr9_&G7I0s*02|*mFbM)Wav>oi0jVS^ zjpPbCMqyYSi;On_2L%We7bWDPBncvLa?*wgMkG8b7vF{c`)eqrd?yis`2gM!fU+<^cRJmT4)Vbl7?sY%D?kCFMZgw^ncHq~re}5GG`vvRh^u+{V1MBw9Cb>PD zf(kjvG0Pu|IoO|;*x7J6KETsj!MITzE32_4Q;#CGtYeSYEn&Rwy)5Q}`)TzS7>6)V)INb*FP6E#P!lK<hr6z_Wxy|Okg*Iar!w(06(JiG504T+@!!jqO8v^SM0o^e!625~bCPV4gX&?Y-F z$=77HIg3c21H-mp@_f4*%>JApi6CpkX1;~IFj-MN(wYPdOgEDp#tm{G=0_u$>w0-V5f zc=ti>#)a51pT3gQP^^XydACsAnMOo@Xo0E5_&U+7$93wU2Cn$3rR?tmVYe#KZf z4e3lYrv)sk_+YbhpGH^wsX$#6YYI}9z$L5m%Ej<9GYoOA{3<{a1@?fyQ)!rE9yZAf zj&QMFHyWS>4E*ZhH#`TCcy6 zm!LwD76{w8L7C~zhwlQhn%aJ&4b-biR+TmZt+F!%#ABRmeo@`&Tj}P;OyLM{ z@}@@4w<9yF4Ke0xW)I;(NhCR-8CV2AHiOPU`x&0%9Lm4nWPsxpLC0Nkt)nZ98R;N4 z5Aofmzswk|LE?lbosJ+C{HlBw3}fdQLW+32979FR^;`+{NagcYBg=(_-0I5}zZD3I zU~~l^8M(5uFc}sw&u0`=p7dTSd%Uf-C35fMcEtc_fM5}=8MS>+$~~~wzj+u z;#U!0Ly!?#T2Md2D*9EyX7-&?V;<>(R`yDP9$yB)O*cmL%JvSEOm^MFo^39jzQ%#G zQojs;_F3B^Y{aq>QVHc(l|YBvi{k0R02!&}xKT~@^pru&#w%}68{}gOVY6^~o|tiP z|Ag201+UFoO2B;|`KF`6O`qKp(pZQxfS`A_t1cRf%dw@(@@`UZ_K5fU&#HV?MTcTTt+SpIpmppI2;GIg!23G|r)tH($WeBl67gi0v$% zF?YK427imK%^!;ScCsZ(=}N)n%2p;0tS51hn(g5eI!J2M?xflG{0c#%3pI%RBF(5+ z{e-C(vy=9%%Lf-pimW6-T+`1}UoZjclJK!3Xn_o%2AjR!khrtZ1hJhm`2E_AGEqm0 zpa#JgCN~#`$DyK>1^P=5W4X8i)o*_G0V6vfcf*}xAB2e?ePOlOv0Sc7EiIv=^SFDYLyPv%!!a}b`=Q`%T?`YUoc&}~D?nE(7+#VcSIgfzE;T9@H8}t; zu@*TO3(>$qcG@Q*?VGIa^Wml9>Y%vuW5N7t6+rE%iU4lqaU~?c|Ftb=4_c!@i*W6_ zDRM6L30@-e6}Ps!^xeqA$t`XIVc%o^$3r`mQjR+-?4(Dl!JbCo-4QQj8z@CD3u=6T zcQUB(^Cl|OMEX!&qoVl9joq7$;_kypR8HO%Ra>;dCa+=Za_CV0EY2m3!u5x>eLZ{E z%dX&n8ZI~}A@=4E2jqT7G}M#%3+VOXbGO%{05y3L2Cc<(|J$vc0-@MuLI2lz_v_gW zQWdM;3(T}P3(-L~FQ>gCBEI;29$$5c-T%>Yq`r}uoK9!Lm+Pj`x%4NFNST)gBY=+Y zlfBYZ_LSO92UrPhG8YqCM_JyhdKHCBE7(Ea*GCxhOo^rkGnA5x(k4h{uBZBZ1INpy zps)Vt4rr{x9MC_cy1ga*$}2U$ zI5DGt_XF=!o5(OtE=!W@1vv8_aOUF9V0!!D-5y!6)v!Q+kf`O!+yZDWzLX*K_>ff{17ba7YYpH6(D-Zkg;K`HBh@lJA=pu95GIVr6UX zkH}T|-G8aMA%qQJJG{l$h9)PPI>m{!#BRiA!>mJLhq%^xs@=mB(KzzchnkSD0t`2^ z)F1hL==I_uVj&_BS1e3UJUsn6vc^+pCG@9zAJ-QtD*7-ip0{c&Oc)r~FlJ{~oX+UI z2O6^(HsTbJ5M;7q@tY=i*=xLk06>-T@g?+x~d9zOc@{yj^F z!{p^bQKw14z^WPZ(e*-X(D(z;Yk9AFS6;k=noRV9wt|A{Rp=dNia$LW23-Z+GVm&I z*0M13FDU!OU9hF#l?B$cNL!yPgVOUphZM!OIEwe2& zS8bYfyM&S9c}bUlsj(f{t)d^p08`=0XLF7aFo64+0G4t@cr^6mN?Ha+a(92Tq?>Gy zo__hPIS5>G)GE`P2Q zcx9+Lk8=Y8!^qE?-Yy3u_Z=CHLv)`ZNfeo)YIoER z@Qp3E^$LxBqi4hDZZ$LKZSG7-owbVhki1dwZCSwS4Deg$-t&Z8m32oWU+r#mC1_$Y zriyjD#i&Q*Ae5m!VWungH+dvro^%NGhZC${W*`zX_FenKLzt`3E6n`>Gy!&w29w9x z)x<$2Jse#stO1Xvsq-bSq?lYj!&tzB!S8-=0}K{OyH;Ob_RCdwN}FHM%MgPfi&HNV zf5~D~Hg|(xgZ2#SSA(S<$D35iii%f#& zm^hpJ0Ol-`4!r^}}^- zP_epY(kjZBbCH)NAUQQE{&^D!VUjpvKipG2TU*}TAO?H&eOuiZUYx)MYd0xdy*J+4 zc;O%!x<})VE5~+oZegUdAVhX{P8WqF0?-;WF%M9IbSU2;+A9dQ37Y|_#&>-fha@?^qcC*9s2W0IK^BCVTjN{=%w9Ee-eQ@#} z!%zlIXEHo2<-9@j6}%9%G(T-9;L{&Y;%~XP(P}KNwWkPNIE4z2YuuXQEpKJf|v#& z{#rx;#0!pN=%DckH|OleoT|}pIBe%ft0Dhinv8AaU71pv$T>Gv=R7O|>(Y||M&kp1 ztO4!OpnfVD@Fq|w)fB^?bAuW&sVFLpY^)V$%7_bfX3T%yBrL29o0rXWb&n=At&MIkL34ZhbkJ6 z5s~;b^s8l$M{skF?g2Lvnign2V+NOTcV*HGp93%s%%U&CJb|8h1bSxd-7~&G&phD* zK@7MDBaFPZ^!{`CuYhigeeKwJI=t`qI6Obzds#SY7+BcSx#=!>{X}lH9bNUz`Qp12 z&2Lrilh`PAJxmMf{LN$rc~tXZt(Ry{5_be00U|26F}_c*1urXSQz<}tyXT%^iWk~v z&Nn@(G*1)nIR;iiDcz!cDnOb^V30gKRArN~Bn+h>*)2!0hdBd)XwQh4(?oY3@CzAX z6lC{tuDSzaJ8vJUnwOq`QVj3z@QEjdgZ^uIeK5I8d~ypqc=GeyoSPz3QlfD?JU;BF z>gh>&KR#$V0j25%s=~QXuuCJ-a9>j$1w23gvRC^?2&RE(c?Y+xpjzy}C#%fs&OktD z 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 Ua%E-;F)}bSF*gb&B}Gq03W;p{`~Uy| literal 0 HcmV?d00001 diff --git a/doc/presentation/main.tex b/doc/presentation/main.tex new file mode 100644 index 0000000..2f0d4f8 --- /dev/null +++ b/doc/presentation/main.tex @@ -0,0 +1,204 @@ +\documentclass{beamer} + +\usepackage{polyglossia} +\usepackage{xcolor} +\usepackage{fontspec} + +\newfontfamily\Rokkitt{Rokkitt.otf} + +\newcommand\gm{{\Rokkitt ghc-mod}\ } +\newcommand\gms{{\Rokkitt ghc-mod's}\ } + +\mode +{ + \usetheme{Rochester} + \usecolortheme{default} +} + +\definecolor{beamer@blendedblue}{HTML}{545488} +\definecolor{gmgrey}{HTML}{F3F3FF} + +\setbeamercolor{normal text}{fg=black,bg=white} +\setbeamercolor{alerted text}{fg=red} +\setbeamercolor{example text}{fg=green!50!black} + +\setbeamercolor{structure}{fg=beamer@blendedblue} + +\setbeamercolor{background canvas}{parent=normal text} +\setbeamercolor{background}{parent=background canvas} + +\setbeamercolor{palette primary}{fg=gmgrey,bg=beamer@blendedblue} % changed this +\setbeamercolor{palette secondary}{use=structure,fg=structure.fg!100!green} % changed this +\setbeamercolor{palette tertiary}{use=structure,fg=structure.fg!100!green} % changed this + +\title{\gm} +\subtitle{Making Haskell development even more fun} + +\author{\includegraphics{logo} \\ \bigskip Daniel Gr\"ober \and Kazu Yamamoto \vspace{-1em} } + +\pgfdeclareimage[height=0.5cm]{logo}{logo} +\logo{\pgfuseimage{logo}} + +% Delete this, if you do not want the table of contents to pop up at +% the beginning of each subsection: +\AtBeginSubsection[] +{ + \begin{frame}{Outline} + \tableofcontents[currentsection,currentsubsection] + \end{frame} +} + +\begin{document} + +\begin{frame} + \titlepage +\end{frame} + +\begin{frame}{Outline} + \tableofcontents +\end{frame} + +\section{Motivation} + +\subsection{What is it?} + +\begin{frame}{What is \gm?} + First some marketing blurb: + + \begin{block}{} + \gm is a backend program for enhancing editors and other kinds of + development environments with support for Haskell, a library for abstracting + the black magic incantations required to use the API of the most popular + Haskell compiler in various build environments and an Emacs Lisp frontend + program to let users access it's features. + \end{block} +\end{frame} + +\begin{frame}{What does it do?} + \begin{itemize} + \item \texttt{check} modules for compilation errors and warnings, + \item get the inferred \texttt{type} of an expression in a module, + \item \texttt{list} modules, compiler and \texttt{lang}uage \texttt{flag}s, + \item \texttt{browse} symbols defined in modules, + \item \texttt{find} which module a symbol was defined in, + \item lookup \texttt{doc}umentation for a symbol or module + \item and a bunch of more obscure things. + \end{itemize} +\end{frame} + +\subsection{Why work on it?} + +\begin{frame}{Why?} + \begin{itemize} + \item It's actually rather popular: \vspace{1em} + + \item GitHub + \includegraphics[width=\textwidth]{gh-stars} + + \item Hackage (Haskell package repository) + \includegraphics[width=\textwidth]{hackage-dls} + + \item Also working with compilers is fun, right? + \end{itemize} +\end{frame} + + +\section{Implementation details} + +\subsection{Current architecture} +\begin{frame}{Current architecture} + \includegraphics[width=\textwidth]{current-architecture} +\end{frame} + +\begin{frame}{\gm the Elisp program} + \begin{itemize} + \item Extends haskell-mode to allow access to \gms features + \item There really isn't much more to it than that + \end{itemize} +\end{frame} + +\begin{frame}{\gm the program} + \begin{itemize} + \item Development environment communicates with \gm process + \item Exists as a one-shot and long running process version + \begin{itemize} + \item \gm simple, doesn't have to worry about caching + \item \gm ``interactive'' much more complex, needs to be very aware of + changing environment and how that affects compiler internal caches + \end{itemize} + \item interactive \gm is generally much faster than \gm at least for features + that require compilation though + \end{itemize} +\end{frame} + +\begin{frame}{\gm the library} + \begin{itemize} + \item \gm frontend programs use the library to implement all functionality + \item Frontends are very thin wrappers around the library, all the + intelligence is in there + \item Primary entry point abstracts away environment setup and just gives the + underlying tool a compiler session to work with + \item Right now it's only of limited use for implementing new \gm like tools + on top of it and definitely needs a redesign (for v6.0 probably) + \item Alan Zimmerman's Haskell Refactorer (HaRe) uses it for example + \end{itemize} +\end{frame} + +\begin{frame}{Problems} + \begin{itemize} + \item Extending \gm from the outside is hard to impossible + \item External tools end up depending on ghc-mod making it difficult for us to + make use of them + \item This all just leads to fragmentation in the already fragmented Haskell + Tooling Landscape + \item one tool, \texttt{mote}, just ended up copy-pasting part of \gms + environment support code straight into it's codebase \texttt{-.-} + \item development environments essentially need to support every tooling + project themselves + \end{itemize} +\end{frame} + +\subsection{Redesigned architecture} +\begin{frame}{Redesigned architecture} + \includegraphics[width=\textwidth]{planned-architecture} +\end{frame} + +\begin{frame}{Redesigned architecture} + \begin{itemize} + \item Factor out commands from library into a seperate package + \item Refine the library so any tool can actually make use of it + \item Design a communication library towards the development environment which + provides some common ground for tools and frontend developers + \end{itemize} +\end{frame} + +\section{The internship} + +\subsection{What we have done so far} + +\begin{frame}{Bitrot} + \begin{itemize} + \item Cabal version 1.22 completely broke \gms hack'y way of getting + information about the build system state + \item To fix this (recurring) problem once and for all we had to completely + re-design how we access Cabal's internal state + \item Next GHC version 7.10 came along and also broke \gm + \item Adding support for the new compiler version was easy + \item Cabal-1.22 support was however still blocking the release + \end{itemize} +\end{frame} + +\subsection{What is still to be done} + +\begin{frame}{TODO} + \begin{itemize} + \item Essentially implement all of the architectural changes + \item Support for implementing REPLs on top of ghc-mod + \item Speed up \gm program by adding network RPC support + \end{itemize} +\end{frame} + +\begin{frame}{Questions?} +\end{frame} + +\end{document} diff --git a/doc/presentation/planned-architecture.png b/doc/presentation/planned-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..46a800f42fedd8780ef119dee3b3ff831659894a GIT binary patch literal 20532 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 X>)5K4@_V3Wg!AIEV##MO|N6fGhzNqg literal 0 HcmV?d00001 From 2a0414f368ee62dfb0f32ce0fb6527f5d5e5c770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 28 Aug 2015 09:44:20 +0200 Subject: [PATCH 085/147] Pass stack ghc paths down to cabal-helper --- Language/Haskell/GhcMod/CabalHelper.hs | 77 ++++++++++++++++++------ Language/Haskell/GhcMod/Debug.hs | 1 + Language/Haskell/GhcMod/Error.hs | 3 + Language/Haskell/GhcMod/PathsAndFiles.hs | 37 +++++++++++- Language/Haskell/GhcMod/Types.hs | 41 +++++++++---- Language/Haskell/GhcMod/Utils.hs | 11 ++++ ghc-mod.cabal | 14 +++-- src/GHCMod.hs | 15 +++-- 8 files changed, 154 insertions(+), 45 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index fac9d1a..9603805 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -34,10 +34,10 @@ import Data.Maybe import Data.Monoid import Data.Serialize (Serialize) import Data.Traversable -import Distribution.Helper +import Distribution.Helper hiding (Programs(..)) +import qualified Distribution.Helper as CH import qualified Language.Haskell.GhcMod.Types as T -import Language.Haskell.GhcMod.Types hiding (ghcProgram, ghcPkgProgram, - cabalProgram) +import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Utils import Language.Haskell.GhcMod.PathsAndFiles @@ -45,6 +45,8 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Output import System.FilePath import System.Directory (findExecutable) +import System.Process +import System.Exit import Prelude hiding ((.)) import Paths_ghc_mod as GhcMod @@ -145,6 +147,18 @@ getStackPackageDbStack = do localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] "" return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb] +patchStackPrograms :: IOish m => Cradle -> Programs -> m Programs +patchStackPrograms crdl progs + | cradleProjectType crdl /= StackProject = return progs +patchStackPrograms crdl progs = do + let projdir = cradleRootDir crdl + Just ghc <- liftIO $ getStackGhcPath projdir + Just ghcPkg <- liftIO $ getStackGhcPkgPath projdir + return $ progs { + ghcProgram = ghc + , ghcPkgProgram = ghcPkg + } + withCabal :: (IOish m, GmEnv m, GmLog m) => m a -> m a withCabal action = do crdl <- cradle @@ -163,7 +177,7 @@ withCabal action = do pkgDbStackOutOfSync <- case mCusPkgDbStack of Just cusPkgDbStack -> do - pkgDb <- runQuery'' readProc (helperProgs opts) projdir distdir $ + pkgDb <- runQuery'' readProc (helperProgs $ programs opts) projdir distdir $ map chPkgToGhcPkg <$> packageDbStack return $ pkgDb /= cusPkgDbStack @@ -185,31 +199,54 @@ withCabal action = do || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ case projType of CabalProject -> - cabalReconfigure readProc opts crdl projdir distdir + cabalReconfigure readProc (programs opts) crdl projdir distdir StackProject -> - -- https://github.com/commercialhaskell/stack/issues/820 - gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build'" + + stackReconfigure crdl (programs opts) _ -> error $ "withCabal: unsupported project type: " ++ show projType action where - cabalReconfigure readProc opts crdl projdir distdir = do + cabalReconfigure readProc progs crdl projdir distdir = do withDirectory_ (cradleRootDir crdl) $ do cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack let progOpts = - [ "--with-ghc=" ++ T.ghcProgram opts ] + [ "--with-ghc=" ++ T.ghcProgram progs ] -- Only pass ghc-pkg if it was actually set otherwise we -- might break cabal's guessing logic - ++ if T.ghcPkgProgram opts /= T.ghcPkgProgram defaultOptions - then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram opts ] + ++ if T.ghcPkgProgram progs /= T.ghcPkgProgram (programs defaultOptions) + then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram progs ] else [] ++ map pkgDbArg cusPkgStack - liftIO $ void $ readProc (T.cabalProgram opts) ("configure":progOpts) "" + liftIO $ void $ readProc (T.cabalProgram progs) ("configure":progOpts) "" gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" liftIO $ writeAutogenFiles readProc projdir distdir + stackReconfigure crdl progs = do + withDirectory_ (cradleRootDir crdl) $ do + supported <- haveStackSupport + if supported + then do + spawn [T.stackProgram progs, "build", "--only-dependencies"] + spawn [T.stackProgram progs, "build", "--only-configure"] + else + gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 1.4.0.0)" + + spawn [] = return () + spawn (exe:args) = do + readProc <- gmReadProcess + liftIO $ void $ readProc exe args "" + + haveStackSupport = do + (rv, _, _) <- + liftIO $ readProcessWithExitCode "stack" ["--numeric-version"] "" + case rv of + ExitSuccess -> return True + ExitFailure _ -> return False + + pkgDbArg :: GhcPkgDb -> String pkgDbArg GlobalDb = "--package-db=global" @@ -233,12 +270,12 @@ isSetupConfigOutOfDate :: Maybe TimedFile -> Maybe TimedFile -> Bool isSetupConfigOutOfDate worldCabalFile worldCabalConfig = do worldCabalConfig < worldCabalFile -helperProgs :: Options -> Programs -helperProgs opts = Programs { - cabalProgram = T.cabalProgram opts, - ghcProgram = T.ghcProgram opts, - ghcPkgProgram = T.ghcPkgProgram opts - } +helperProgs :: Programs -> CH.Programs +helperProgs progs = CH.Programs { + cabalProgram = T.cabalProgram progs, + ghcProgram = T.ghcProgram progs, + ghcPkgProgram = T.ghcPkgProgram progs + } chCached :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m, Serialize a) => (FilePath -> Cached m GhcModState ChCacheData a) -> m a @@ -252,7 +289,9 @@ chCached c = do -- changes the cache files will be gone anyways ;) cacheInputData root = do opt <- options - return $ ( helperProgs opt + crdl <- cradle + progs <- patchStackPrograms crdl (programs opt) + return $ ( helperProgs progs , root , (gmVer, chVer) ) diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 2441ec9..a1acd85 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -53,6 +53,7 @@ cabalDebug = do return $ [ "Cabal file: " ++ show cradleCabalFile + , "Cabal Project Type: " ++ show cradleProjectType , "Cabal entrypoints:\n" ++ render (nest 4 $ mapDoc gmComponentNameDoc smpDoc entrypoints) , "Cabal components:\n" ++ render (nest 4 $ diff --git a/Language/Haskell/GhcMod/Error.hs b/Language/Haskell/GhcMod/Error.hs index 965aa7e..3a3c786 100644 --- a/Language/Haskell/GhcMod/Error.hs +++ b/Language/Haskell/GhcMod/Error.hs @@ -140,6 +140,9 @@ gmeDoc e = case e of ++ intercalate "\", \"" cfs ++"\"." GMECabalStateFile csfe -> gmCsfeDoc csfe + GMEStackBootrap rv stderr -> + (text $ "Boostrapping stack project failed (exited with "++show rv++")") + <+>: text stderr ghcExceptionDoc :: GhcException -> Doc ghcExceptionDoc e@(CmdLineError _) = diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 0639e24..1b75940 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -31,6 +31,8 @@ import Distribution.Helper (buildPlatform) import System.Directory import System.FilePath import System.Process +import System.Info.Extra +import System.Exit import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Error @@ -76,9 +78,38 @@ findStackConfigFile :: FilePath -> IO (Maybe FilePath) findStackConfigFile dir = mightExist (dir "stack.yaml") getStackDistDir :: FilePath -> IO (Maybe FilePath) -getStackDistDir dir = U.withDirectory_ dir $ runMaybeT $ do - stack <- MaybeT $ findExecutable "stack" - liftIO $ takeWhile (/='\n') <$> readProcess stack ["path", "--dist-dir"] "" +getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do + takeWhile (/='\n') <$> readStack ["path", "--dist-dir"] + +getStackGhcPath :: FilePath -> IO (Maybe FilePath) +getStackGhcPath = findExecutablesInStackBinPath "ghc" + +getStackGhcPkgPath :: FilePath -> IO (Maybe FilePath) +getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" + +findExecutablesInStackBinPath :: String -> FilePath -> IO (Maybe FilePath) +findExecutablesInStackBinPath exe projdir = + U.withDirectory_ projdir $ runMaybeT $ do + path <- splitSearchPath . takeWhile (/='\n') + <$> readStack ["path", "--bin-path"] + MaybeT $ listToMaybe <$> findExecutablesInDirectories' path exe + +findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] +findExecutablesInDirectories' path binary = + U.findFilesWith' isExecutable path (binary <.> exeExtension) + where isExecutable file = do + perms <- getPermissions file + return $ executable perms + + exeExtension = if isWindows then "exe" else "" + +readStack :: [String] -> MaybeT IO String +readStack args = do + stack <- MaybeT $ findExecutable "stack" + (e, out, err) <- liftIO $ readProcessWithExitCode stack args "" + case e of + ExitSuccess -> return out + (ExitFailure rv) -> throw $ GMEStackBootrap rv err -- | Get path to sandbox config file getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 8525503..ff893fb 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, +{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, RankNTypes, StandaloneDeriving, DefaultSignatures, FlexibleInstances, TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-} module Language.Haskell.GhcMod.Types ( @@ -27,7 +27,8 @@ import Data.Maybe import Data.Typeable (Typeable) import Data.IORef import Data.Label.Derive -import Distribution.Helper +import Distribution.Helper hiding (Programs(..)) +import qualified Distribution.Helper as CabalHelper import Exception (ExceptionMonad) #if __GLASGOW_HASKELL__ < 708 import qualified MonadUtils as GHC (MonadIO(..)) @@ -74,6 +75,19 @@ data FileMapping = FileMapping {fmPath :: FilePath, fmTemp :: Bool} type FileMappingMap = Map FilePath FileMapping +data ProgramSource = ProgramSourceUser | ProgramSourceStack + +data Programs = Programs { + -- | @ghc@ program name. + ghcProgram :: FilePath + -- | @ghc-pkg@ program name. + , ghcPkgProgram :: FilePath + -- | @cabal@ program name. + , cabalProgram :: FilePath + -- | @stack@ program name. + , stackProgram :: FilePath + } deriving (Show) + data Options = Options { outputStyle :: OutputStyle -- | Line separator string. @@ -83,12 +97,7 @@ data Options = Options { , linePrefix :: Maybe (String, String) -- | Verbosity , logLevel :: GmLogLevel - -- | @ghc@ program name. - , ghcProgram :: FilePath - -- | @ghc-pkg@ program name. - , ghcPkgProgram :: FilePath - -- | @cabal@ program name. - , cabalProgram :: FilePath + , programs :: Programs -- | GHC command line options set on the @ghc-mod@ command line , ghcUserOptions:: [GHCOption] -- | If 'True', 'browse' also returns operators. @@ -108,9 +117,12 @@ defaultOptions = Options { , lineSeparator = LineSeparator "\0" , linePrefix = Nothing , logLevel = GmWarning - , ghcProgram = "ghc" - , ghcPkgProgram = "ghc-pkg" - , cabalProgram = "cabal" + , programs = Programs { + ghcProgram = "ghc" + , ghcPkgProgram = "ghc-pkg" + , cabalProgram = "cabal" + , stackProgram = "stack" + } , ghcUserOptions = [] , operators = False , detailed = False @@ -366,6 +378,9 @@ data GhcModError | GMECabalStateFile GMConfigStateFileError -- ^ Reading Cabal's state configuration file falied somehow. + + | GMEStackBootrap Int String + -- ^ Bootstrapping @stack@ environment failed (process exited with failure) deriving (Eq,Show,Typeable) instance Error GhcModError where @@ -386,10 +401,12 @@ data GMConfigStateFileError deriving instance Generic Version instance Serialize Version -instance Serialize Programs +instance Serialize CabalHelper.Programs instance Serialize ChModuleName instance Serialize ChComponentName instance Serialize ChEntrypoint mkLabel ''GhcModCaches mkLabel ''GhcModState +mkLabel ''Options +mkLabel ''Programs diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 2ee4e4d..d4f8043 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -197,3 +197,14 @@ mkRevRedirMapFunc = do where mf :: FilePath -> FileMapping -> (FilePath, FilePath) mf from to = (fmPath to, from) + +findFilesWith' :: (FilePath -> IO Bool) -> [FilePath] -> String -> IO [FilePath] +findFilesWith' _ [] _ = return [] +findFilesWith' f (d:ds) fileName = do + let file = d fileName + exist <- doesFileExist file + b <- if exist then f file else return False + if b then do + files <- findFilesWith' f ds fileName + return $ file : files + else findFilesWith' f ds fileName diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 500a235..d24f528 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -98,11 +98,9 @@ Library GHC-Options: -Wall -fno-warn-deprecations Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns, ConstraintKinds, FlexibleContexts, - DataKinds, KindSignatures, TypeOperators + DataKinds, KindSignatures, TypeOperators, ViewPatterns Exposed-Modules: Language.Haskell.GhcMod Language.Haskell.GhcMod.Internal - Other-Modules: Paths_ghc_mod - Utils Language.Haskell.GhcMod.Boot Language.Haskell.GhcMod.Browse Language.Haskell.GhcMod.CabalHelper @@ -142,6 +140,8 @@ Library Language.Haskell.GhcMod.Types Language.Haskell.GhcMod.Utils Language.Haskell.GhcMod.World + Other-Modules: Paths_ghc_mod + Utils Build-Depends: base >= 4.0 && < 5 , bytestring , cereal >= 0.4 @@ -169,7 +169,8 @@ Library , haskell-src-exts , text , djinn-ghc >= 0.0.2.2 - , fclabels + , fclabels == 2.0.* + , extra == 1.4.* if impl(ghc < 7.8) Build-Depends: convertible if impl(ghc < 7.5) @@ -181,7 +182,7 @@ Executable ghc-mod Default-Language: Haskell2010 Main-Is: GHCMod.hs Other-Modules: Paths_ghc_mod - GHC-Options: -Wall -fno-warn-deprecations + GHC-Options: -Wall -fno-warn-deprecations -threaded Default-Extensions: ConstraintKinds, FlexibleContexts HS-Source-Dirs: src Build-Depends: base >= 4.0 && < 5 @@ -194,6 +195,7 @@ Executable ghc-mod , mtl >= 2.0 , ghc , ghc-mod + , fclabels == 2.0.* Executable ghc-modi Default-Language: Haskell2010 @@ -229,7 +231,7 @@ Test-Suite spec Default-Language: Haskell2010 Default-Extensions: ScopedTypeVariables, RecordWildCards, NamedFieldPuns, ConstraintKinds, FlexibleContexts, - DataKinds, KindSignatures, TypeOperators + DataKinds, KindSignatures, TypeOperators, ViewPatterns Main-Is: Main.hs Hs-Source-Dirs: test, . Ghc-Options: -Wall -fno-warn-deprecations diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 149d8c0..f27fc03 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -3,12 +3,13 @@ module Main where import Config (cProjectVersion) -import MonadUtils (liftIO) +import Control.Category import Control.Applicative import Control.Arrow import Control.Monad import Data.Typeable (Typeable) import Data.Version (showVersion) +import Data.Label import Data.List import Data.List.Split import Data.Char (isSpace) @@ -16,6 +17,7 @@ import Data.Maybe import Exception import Language.Haskell.GhcMod import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO) +import Language.Haskell.GhcMod.Types import Paths_ghc_mod import System.Console.GetOpt (OptDescr(..), ArgDescr(..), ArgOrder(..)) import qualified System.Console.GetOpt as O @@ -26,7 +28,7 @@ import System.Environment (getArgs) import System.IO (stdout, hSetEncoding, utf8, hFlush) import System.Exit import Text.PrettyPrint -import Prelude +import Prelude hiding ((.)) import Misc @@ -313,13 +315,16 @@ Exposed functions: Right $ o { fileMappings = m : fileMappings o } , option "" ["with-ghc"] "GHC executable to use" $ - reqArg "PROG" $ \p o -> Right $ o { ghcProgram = p } + reqArg "PATH" $ \p o -> Right $ set (lGhcProgram . lPrograms) p o , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $ - reqArg "PROG" $ \p o -> Right $ o { ghcPkgProgram = p } + reqArg "PATH" $ \p o -> Right $ set (lGhcPkgProgram . lPrograms) p o , option "" ["with-cabal"] "cabal-install executable to use" $ - reqArg "PROG" $ \p o -> Right $ o { cabalProgram = p } + reqArg "PATH" $ \p o -> Right $ set (lCabalProgram . lPrograms) p o + + , option "" ["with-stack"] "stack executable to use" $ + reqArg "PATH" $ \p o -> Right $ set (lStackProgram . lPrograms) p o , option "" ["version"] "print version information" $ NoArg $ \_ -> Left ["version"] From e23e68279321e719b273f90fb1b7f6aed10cc534 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 31 Aug 2015 14:13:52 +0900 Subject: [PATCH 086/147] Revert "Serialize check-syntax" (#567) This reverts commit 3bf4243bafc2c5713da6c63851e1a2d0a206d768. --- elisp/ghc-check.el | 10 +++------- elisp/ghc.el | 3 --- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index 4b85773..f795c58 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -66,14 +66,10 @@ nil do not display errors/warnings. (interactive) ;; Only check syntax of visible buffers (when (and (buffer-file-name) - (file-exists-p (buffer-file-name)) - (get-buffer-window (current-buffer) t)) - (with-timeout - (10 (error "ghc process may have hung or exited with an error")) - (while ghc-process-running (sleep-for 0.1))) + (file-exists-p (buffer-file-name))) (ghc-with-process (ghc-check-send) - 'ghc-check-callback - (lambda () (setq mode-line-process " -:-"))))) + 'ghc-check-callback + (lambda () (setq mode-line-process " -:-"))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/elisp/ghc.el b/elisp/ghc.el index 0c4f967..d1d7e1d 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -119,9 +119,6 @@ (setq ghc-initialized t) (defadvice save-buffer (after ghc-check-syntax-on-save activate) "Check syntax with GHC when a haskell-mode buffer is saved." - (when (eq 'haskell-mode major-mode) (ghc-check-syntax))) - (defadvice switch-to-buffer (after ghc-check-syntax-on-switch-to-buffer activate) - "Check syntax with GHC when switching to a haskell-mode buffer." (when (eq 'haskell-mode major-mode) (ghc-check-syntax)))) (ghc-import-module) (ghc-check-syntax)) From 0b65487e508cf972c9fa763cc2210496cb6c0569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 31 Aug 2015 07:33:36 +0200 Subject: [PATCH 087/147] Stderr output pre-GhcModT for stack cradle --- Language/Haskell/GhcMod/CabalHelper.hs | 18 +++-- Language/Haskell/GhcMod/CaseSplit.hs | 8 +- Language/Haskell/GhcMod/Convert.hs | 94 ++++++++++++------------ Language/Haskell/GhcMod/Cradle.hs | 16 ++-- Language/Haskell/GhcMod/Error.hs | 6 +- Language/Haskell/GhcMod/FillSig.hs | 40 +++++----- Language/Haskell/GhcMod/Info.hs | 2 +- Language/Haskell/GhcMod/Logger.hs | 5 +- Language/Haskell/GhcMod/Monad.hs | 17 +++-- Language/Haskell/GhcMod/Output.hs | 32 +++++--- Language/Haskell/GhcMod/PathsAndFiles.hs | 32 ++++---- Language/Haskell/GhcMod/Types.hs | 25 ++++--- src/GHCMod.hs | 55 +++++++------- 13 files changed, 189 insertions(+), 161 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 9603805..b339a12 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -147,13 +147,13 @@ getStackPackageDbStack = do localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] "" return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb] -patchStackPrograms :: IOish m => Cradle -> Programs -> m Programs -patchStackPrograms crdl progs +patchStackPrograms :: IOish m => OutputOpts -> Cradle -> Programs -> m Programs +patchStackPrograms _oopts crdl progs | cradleProjectType crdl /= StackProject = return progs -patchStackPrograms crdl progs = do +patchStackPrograms oopts crdl progs = do let projdir = cradleRootDir crdl - Just ghc <- liftIO $ getStackGhcPath projdir - Just ghcPkg <- liftIO $ getStackGhcPkgPath projdir + Just ghc <- liftIO $ getStackGhcPath oopts projdir + Just ghcPkg <- liftIO $ getStackGhcPkgPath oopts projdir return $ progs { ghcProgram = ghc , ghcPkgProgram = ghcPkg @@ -288,10 +288,12 @@ chCached c = do -- we don't need to include the disdir in the cache input because when it -- changes the cache files will be gone anyways ;) cacheInputData root = do - opt <- options + opts <- options + let oopts = outputOpts opts + progs = programs opts crdl <- cradle - progs <- patchStackPrograms crdl (programs opt) - return $ ( helperProgs progs + progs' <- patchStackPrograms oopts crdl progs + return $ ( helperProgs progs' , root , (gmVer, chVer) ) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 1db05d7..3283e30 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -6,9 +6,11 @@ module Language.Haskell.GhcMod.CaseSplit ( import Data.List (find, intercalate) import Data.Maybe (isJust) +import Data.Functor import qualified Data.Text as T import qualified Data.Text.IO as T (readFile) import System.FilePath +import Prelude import qualified DataCon as Ty import GHC (GhcMonad, LPat, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) @@ -48,12 +50,12 @@ splits :: IOish m -> GhcModT m String splits file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ do - opt <- options + oopts <- outputOpts <$> options crdl <- cradle style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping (cradleCurrentDir crdl file) - whenFound' opt (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of + whenFound' oopts (getSrcSpanTypeForSplit modSum lineNo colNo) $ \x -> case x of (SplitInfo varName bndLoc (varLoc,varT) _matches) -> do let varName' = showName dflag style varName -- Convert name to string t <- genCaseSplitTextFile file (SplitToTextInfo varName' bndLoc varLoc $ @@ -68,7 +70,7 @@ splits file lineNo colNo = handler (SomeException ex) = do gmLog GmException "splits" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< options + emptyResult =<< outputOpts <$> options ---------------------------------------------------------------- -- a. Code for getting the information of the variable diff --git a/Language/Haskell/GhcMod/Convert.hs b/Language/Haskell/GhcMod/Convert.hs index 2715696..a17abd0 100644 --- a/Language/Haskell/GhcMod/Convert.hs +++ b/Language/Haskell/GhcMod/Convert.hs @@ -25,25 +25,25 @@ inter _ [] = id inter c bs = foldr1 (\x y -> x . (c:) . y) bs convert' :: (ToString a, IOish m, GmEnv m) => a -> m String -convert' x = flip convert x <$> options +convert' x = flip convert x . outputOpts <$> options -convert :: ToString a => Options -> a -> String -convert opt@Options { outputStyle = LispStyle } x = toLisp opt x "\n" -convert opt@Options { outputStyle = PlainStyle } x +convert :: ToString a => OutputOpts -> a -> String +convert opt@OutputOpts { outputStyle = LispStyle } x = toLisp opt x "\n" +convert opt@OutputOpts { outputStyle = PlainStyle } x | str == "\n" = "" | otherwise = str where str = toPlain opt x "\n" class ToString a where - toLisp :: Options -> a -> Builder - toPlain :: Options -> a -> Builder + toLisp :: OutputOpts -> a -> Builder + toPlain :: OutputOpts -> a -> Builder -lineSep :: Options -> String -lineSep opt = interpret lsep +lineSep :: OutputOpts -> String +lineSep oopts = interpret lsep where interpret s = read $ "\"" ++ s ++ "\"" - LineSeparator lsep = lineSeparator opt + LineSeparator lsep = lineSeparator oopts -- | -- @@ -52,8 +52,8 @@ lineSep opt = interpret lsep -- >>> toPlain defaultOptions "foo" "" -- "foo" instance ToString String where - toLisp opt = quote opt - toPlain opt = replace '\n' (lineSep opt) + toLisp oopts = quote oopts + toPlain oopts = replace '\n' (lineSep oopts) -- | -- @@ -62,12 +62,12 @@ instance ToString String where -- >>> toPlain defaultOptions ["foo", "bar", "baz"] "" -- "foo\nbar\nbaz" instance ToString [String] where - toLisp opt = toSexp1 opt - toPlain opt = inter '\n' . map (toPlain opt) + toLisp oopts = toSexp1 oopts + toPlain oopts = inter '\n' . map (toPlain oopts) instance ToString [ModuleString] where - toLisp opt = toLisp opt . map getModuleString - toPlain opt = toPlain opt . map getModuleString + toLisp oopts = toLisp oopts . map getModuleString + toPlain oopts = toPlain oopts . map getModuleString -- | -- @@ -77,47 +77,47 @@ instance ToString [ModuleString] where -- >>> toPlain defaultOptions inp "" -- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\"" instance ToString [((Int,Int,Int,Int),String)] where - toLisp opt = toSexp2 . map toS + toLisp oopts = toSexp2 . map toS where - toS x = ('(' :) . tupToString opt x . (')' :) - toPlain opt = inter '\n' . map (tupToString opt) + toS x = ('(' :) . tupToString oopts x . (')' :) + toPlain oopts = inter '\n' . map (tupToString oopts) instance ToString ((Int,Int,Int,Int),String) where - toLisp opt x = ('(' :) . tupToString opt x . (')' :) - toPlain opt x = tupToString opt x + toLisp oopts x = ('(' :) . tupToString oopts x . (')' :) + toPlain oopts x = tupToString oopts x instance ToString ((Int,Int,Int,Int),[String]) where - toLisp opt (x,s) = ('(' :) . fourIntsToString opt x . - (' ' :) . toLisp opt s . (')' :) - toPlain opt (x,s) = fourIntsToString opt x . ('\n' :) . toPlain opt s + toLisp oopts (x,s) = ('(' :) . fourIntsToString x . + (' ' :) . toLisp oopts s . (')' :) + toPlain oopts (x,s) = fourIntsToString x . ('\n' :) . toPlain oopts s instance ToString (String, (Int,Int,Int,Int),[String]) where - toLisp opt (s,x,y) = toSexp2 [toLisp opt s, ('(' :) . fourIntsToString opt x . (')' :), toLisp opt y] - toPlain opt (s,x,y) = inter '\n' [toPlain opt s, fourIntsToString opt x, toPlain opt y] + toLisp oopts (s,x,y) = toSexp2 [toLisp oopts s, ('(' :) . fourIntsToString x . (')' :), toLisp oopts y] + toPlain oopts (s,x,y) = inter '\n' [toPlain oopts s, fourIntsToString x, toPlain oopts y] -toSexp1 :: Options -> [String] -> Builder -toSexp1 opt ss = ('(' :) . inter ' ' (map (quote opt) ss) . (')' :) +toSexp1 :: OutputOpts -> [String] -> Builder +toSexp1 oopts ss = ('(' :) . inter ' ' (map (quote oopts) ss) . (')' :) toSexp2 :: [Builder] -> Builder toSexp2 ss = ('(' :) . inter ' ' ss . (')' :) -fourIntsToString :: Options -> (Int,Int,Int,Int) -> Builder -fourIntsToString _ (a,b,c,d) = (show a ++) . (' ' :) - . (show b ++) . (' ' :) - . (show c ++) . (' ' :) - . (show d ++) +fourIntsToString :: (Int,Int,Int,Int) -> Builder +fourIntsToString (a,b,c,d) = (show a ++) . (' ' :) + . (show b ++) . (' ' :) + . (show c ++) . (' ' :) + . (show d ++) -tupToString :: Options -> ((Int,Int,Int,Int),String) -> Builder -tupToString opt ((a,b,c,d),s) = (show a ++) . (' ' :) - . (show b ++) . (' ' :) - . (show c ++) . (' ' :) - . (show d ++) . (' ' :) - . quote opt s -- fixme: quote is not necessary +tupToString :: OutputOpts -> ((Int,Int,Int,Int),String) -> Builder +tupToString oopts ((a,b,c,d),s) = (show a ++) . (' ' :) + . (show b ++) . (' ' :) + . (show c ++) . (' ' :) + . (show d ++) . (' ' :) + . quote oopts s -- fixme: quote is not necessary -quote :: Options -> String -> Builder -quote opt str = ("\"" ++) . (quote' str ++) . ("\"" ++) +quote :: OutputOpts -> String -> Builder +quote oopts str = ("\"" ++) . (quote' str ++) . ("\"" ++) where - lsep = lineSep opt + lsep = lineSep oopts quote' [] = [] quote' (x:xs) | x == '\n' = lsep ++ quote' xs @@ -128,13 +128,13 @@ quote opt str = ("\"" ++) . (quote' str ++) . ("\"" ++) ---------------------------------------------------------------- -- Empty result to be returned when no info can be gathered -emptyResult :: Monad m => Options -> m String -emptyResult opt = return $ convert opt ([] :: [String]) +emptyResult :: Monad m => OutputOpts -> m String +emptyResult oopts = return $ convert oopts ([] :: [String]) -- Return an emptyResult when Nothing -whenFound :: (Monad m, ToString b) => Options -> m (Maybe a) -> (a -> b) -> m String -whenFound opt from f = maybe (emptyResult opt) (return . convert opt . f) =<< from +whenFound :: (Monad m, ToString b) => OutputOpts -> m (Maybe a) -> (a -> b) -> m String +whenFound oopts from f = maybe (emptyResult oopts) (return . convert oopts . f) =<< from -- Return an emptyResult when Nothing, inside a monad -whenFound' :: (Monad m, ToString b) => Options -> m (Maybe a) -> (a -> m b) -> m String -whenFound' opt from f = maybe (emptyResult opt) (\x -> do y <- f x ; return (convert opt y)) =<< from +whenFound' :: (Monad m, ToString b) => OutputOpts -> m (Maybe a) -> (a -> m b) -> m String +whenFound' oopts from f = maybe (emptyResult oopts) (\x -> do y <- f x ; return (convert oopts y)) =<< from diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index b348b89..7a64d26 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -29,12 +29,12 @@ import Prelude -- Find a cabal file by tracing ancestor directories. -- Find a sandbox according to a cabal sandbox config -- in a cabal directory. -findCradle :: IO Cradle -findCradle = findCradle' =<< getCurrentDirectory +findCradle :: OutputOpts -> IO Cradle +findCradle oopts = findCradle' oopts =<< getCurrentDirectory -findCradle' :: FilePath -> IO Cradle -findCradle' dir = run $ do - (stackCradle dir `mplus` cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir) +findCradle' :: OutputOpts -> FilePath -> IO Cradle +findCradle' oopts dir = run $ do + (stackCradle oopts dir `mplus` cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir) where run a = fillTempDir =<< (fromJust <$> runMaybeT a) findSpecCradle :: FilePath -> IO Cradle @@ -73,8 +73,8 @@ cabalCradle wdir = do , cradleDistDir = "dist" } -stackCradle :: FilePath -> MaybeT IO Cradle -stackCradle wdir = do +stackCradle :: OutputOpts -> FilePath -> MaybeT IO Cradle +stackCradle oopts wdir = do cabalFile <- MaybeT $ findCabalFile wdir let cabalDir = takeDirectory cabalFile @@ -85,7 +85,7 @@ stackCradle wdir = do -- rather than stack, or maybe that's just me ;) whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero - distDir <- MaybeT $ getStackDistDir cabalDir + distDir <- MaybeT $ getStackDistDir oopts cabalDir return Cradle { cradleProjectType = StackProject diff --git a/Language/Haskell/GhcMod/Error.hs b/Language/Haskell/GhcMod/Error.hs index 3a3c786..2bb3f4e 100644 --- a/Language/Haskell/GhcMod/Error.hs +++ b/Language/Haskell/GhcMod/Error.hs @@ -140,9 +140,9 @@ gmeDoc e = case e of ++ intercalate "\", \"" cfs ++"\"." GMECabalStateFile csfe -> gmCsfeDoc csfe - GMEStackBootrap rv stderr -> - (text $ "Boostrapping stack project failed (exited with "++show rv++")") - <+>: text stderr + GMEStackBootrap msg -> + (text $ "Boostrapping stack project failed") + <+>: text msg ghcExceptionDoc :: GhcException -> Doc ghcExceptionDoc e@(CmdLineError _) = diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index ed1a769..f3c03a5 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -9,15 +9,27 @@ module Language.Haskell.GhcMod.FillSig ( import Data.Char (isSymbol) import Data.Function (on) +import Data.Functor import Data.List (find, nub, sortBy) import qualified Data.Map as M import Data.Maybe (catMaybes) import Text.PrettyPrint (($$), text, nest) +import Prelude + import Exception (ghandle, SomeException(..)) import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L)) import qualified GHC as G import qualified Name as G +import Outputable (PprStyle) +import qualified Type as Ty +import qualified HsBinds as Ty +import qualified Class as Ty +import qualified Var as Ty +import qualified HsPat as Ty +import qualified Language.Haskell.Exts.Annotated as HE +import Djinn.GHC + import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.DynFlags @@ -28,14 +40,6 @@ import Language.Haskell.GhcMod.Pretty (showDoc) import Language.Haskell.GhcMod.Doc import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.FileMapping (fileModSummaryWithMapping) -import Outputable (PprStyle) -import qualified Type as Ty -import qualified HsBinds as Ty -import qualified Class as Ty -import qualified Var as Ty -import qualified HsPat as Ty -import qualified Language.Haskell.Exts.Annotated as HE -import Djinn.GHC #if __GLASGOW_HASKELL__ >= 710 import GHC (unLoc) @@ -74,11 +78,11 @@ sig :: IOish m -> GhcModT m String sig file lineNo colNo = runGmlT' [Left file] deferErrors $ ghandle fallback $ do - opt <- options + oopts <- outputOpts <$> options style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file - whenFound opt (getSignature modSum lineNo colNo) $ \s -> + whenFound oopts (getSignature modSum lineNo colNo) $ \s -> case s of Signature loc names ty -> ("function", fourInts loc, map (initialBody dflag style ty) names) @@ -93,10 +97,10 @@ sig file lineNo colNo = in (rTy, fourInts loc, [initial ++ body]) where fallback (SomeException _) = do - opt <- options + oopts <- outputOpts <$> options -- Code cannot be parsed by ghc module -- Fallback: try to get information via haskell-src-exts - whenFound opt (getSignatureFromHE file lineNo colNo) $ \x -> case x of + whenFound oopts (getSignatureFromHE file lineNo colNo) $ \x -> case x of HESignature loc names ty -> ("function", fourIntsHE loc, map (initialBody undefined undefined ty) names) HEFamSignature loc flavour name vars -> @@ -343,14 +347,14 @@ refine :: IOish m refine file lineNo colNo (Expression expr) = ghandle handler $ runGmlT' [Left file] deferErrors $ do - opt <- options + oopts <- outputOpts <$> options style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file p <- G.parseModule modSum tcm@TypecheckedModule{tm_typechecked_source = tcs} <- G.typecheckModule p ety <- G.exprType expr - whenFound opt (findVar dflag style tcm tcs lineNo colNo) $ + whenFound oopts (findVar dflag style tcm tcs lineNo colNo) $ \(loc, name, rty, paren) -> let eArgs = getFnArgs ety rArgs = getFnArgs rty @@ -363,7 +367,7 @@ refine file lineNo colNo (Expression expr) = handler (SomeException ex) = do gmLog GmException "refining" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< options + emptyResult =<< outputOpts <$> options -- Look for the variable in the specified position findVar @@ -420,7 +424,7 @@ auto :: IOish m -> GhcModT m String auto file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ do - opt <- options + oopts <- outputOpts <$> options style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file @@ -429,7 +433,7 @@ auto file lineNo colNo = tm_typechecked_source = tcs , tm_checked_module_info = minfo } <- G.typecheckModule p - whenFound' opt (findVar dflag style tcm tcs lineNo colNo) $ \(loc, _name, rty, paren) -> do + whenFound' oopts (findVar dflag style tcm tcs lineNo colNo) $ \(loc, _name, rty, paren) -> do topLevel <- getEverythingInTopLevel minfo let (f,pats) = getPatsForVariable tcs (lineNo,colNo) -- Remove self function to prevent recursion, and id to trim @@ -452,7 +456,7 @@ auto file lineNo colNo = handler (SomeException ex) = do gmLog GmException "auto-refining" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< options + emptyResult =<< outputOpts <$> options -- Functions we do not want in completions notWantedFuns :: [String] diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index e952838..b3f1c52 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -36,7 +36,7 @@ info file expr = ghandle handler $ runGmlT' [Left file] deferErrors $ withInteractiveContext $ - convert <$> options <*> body + convert . outputOpts <$> options <*> body where handler (SomeException ex) = do gmLog GmException "info" $ text "" $$ nest 4 (showDoc ex) diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index 2cb3247..ef5b556 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -30,6 +30,7 @@ import Language.Haskell.GhcMod.DynFlags (withDynFlags) import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Utils (mkRevRedirMapFunc) +import Language.Haskell.GhcMod.Types import qualified Language.Haskell.GhcMod.Gap as Gap import Prelude @@ -81,8 +82,8 @@ withLogger :: (GmGhc m, GmEnv m, GmState m) -> m (Either String (String, a)) withLogger f action = do env <- G.getSession - opts <- options - let conv = convert opts + oopts <- outputOpts <$> options + let conv = convert oopts eres <- withLogger' env $ \setDf -> withDynFlags (f . setDf) action return $ either (Left . conv) (Right . first conv) eres diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index adc7114..172b296 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -51,21 +51,22 @@ import Exception (ExceptionMonad(..)) import System.Directory import Prelude -withCradle :: IOish m => FilePath -> (Cradle -> m a) -> m a -withCradle cradledir f = - gbracket (liftIO $ findCradle' cradledir) (liftIO . cleanupCradle) f +withCradle :: IOish m => OutputOpts -> FilePath -> (Cradle -> m a) -> m a +withCradle oopts cradledir f = + gbracket (liftIO $ findCradle' oopts cradledir) (liftIO . cleanupCradle) f withGhcModEnv :: IOish m => FilePath -> Options -> (GhcModEnv -> m a) -> m a -withGhcModEnv dir opt f = withCradle dir (withGhcModEnv' opt f) +withGhcModEnv dir opts f = + withCradle (outputOpts opts) dir (withGhcModEnv' opts f) withGhcModEnv' :: IOish m => Options -> (GhcModEnv -> m a) -> Cradle -> m a -withGhcModEnv' opt f crdl = do +withGhcModEnv' opts f crdl = do olddir <- liftIO getCurrentDirectory c <- liftIO newChan - let outp = case linePrefix opt of + let outp = case linePrefix $ outputOpts opts of Just _ -> GmOutputChan c Nothing -> GmOutputStdio - gbracket_ (setup c) (teardown olddir) (f $ GhcModEnv opt crdl outp) + gbracket_ (setup c) (teardown olddir) (f $ GhcModEnv opts crdl outp) where setup c = liftIO $ do setCurrentDirectory $ cradleRootDir crdl @@ -94,7 +95,7 @@ runGhcModT' :: IOish m runGhcModT' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> withGhcModEnv dir' opt $ \env -> first (fst <$>) <$> runGhcModT'' env defaultGhcModState - (gmSetLogLevel (logLevel opt) >> action) + (gmSetLogLevel (logLevel $ outputOpts opt) >> action) -- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT -- computation. Note that if the computation that returned @result@ modified the diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 6e3f36c..6115707 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -22,9 +22,10 @@ module Language.Haskell.GhcMod.Output ( , gmErrStr , gmPutStrLn , gmErrStrLn + , gmReadProcess , gmUnsafePutStrLn , gmUnsafeErrStrLn - , gmReadProcess + , gmUnsafeReadProcess , stdoutGateway ) where @@ -36,6 +37,7 @@ import Control.Monad import Control.DeepSeq import Control.Exception import Control.Concurrent +import Prelude import Language.Haskell.GhcMod.Types hiding (LineSeparator) import Language.Haskell.GhcMod.Monad.Types @@ -65,16 +67,16 @@ toGmLines s = GmLines GmPartial s outputFns :: (GmEnv m, MonadIO m') => m (GmLines String -> m' (), GmLines String -> m' ()) outputFns = do - opts <- options + oopts <- outputOpts `liftM` options env <- gmeAsk - return $ outputFns' opts (gmOutput env) + return $ outputFns' oopts (gmOutput env) outputFns' :: MonadIO m' - => Options + => OutputOpts -> GmOutput -> (GmLines String -> m' (), GmLines String -> m' ()) outputFns' opts output = let - Options {..} = opts + OutputOpts {..} = opts pfx f = withLines f @@ -108,9 +110,14 @@ gmErrStr str = do -- | Only use these when you're sure there are no other writers on stdout gmUnsafePutStrLn, gmUnsafeErrStrLn - :: MonadIO m => Options -> String -> m () -gmUnsafePutStrLn opts = (fst $ outputFns' opts GmOutputStdio) . toGmLines -gmUnsafeErrStrLn opts = (snd $ outputFns' opts GmOutputStdio) . toGmLines + :: MonadIO m => OutputOpts -> String -> m () +gmUnsafePutStrLn oopts = (fst $ outputFns' oopts GmOutputStdio) . toGmLines +gmUnsafeErrStrLn oopts = (snd $ outputFns' oopts GmOutputStdio) . toGmLines + +gmUnsafeReadProcess :: OutputOpts -> FilePath -> [String] -> String -> IO String +gmUnsafeReadProcess oopts = + readProcessStderrChan' (snd $ outputFns' oopts GmOutputStdio) + gmReadProcess :: GmEnv m => m (FilePath -> [String] -> String -> IO String) gmReadProcess = do @@ -146,8 +153,13 @@ stdoutGateway chan = go ("", "") readProcessStderrChan :: GmEnv m => m (FilePath -> [String] -> String -> IO String) readProcessStderrChan = do - (_, e) <- outputFns - return $ go e + (_, e :: GmLines String -> IO ()) <- outputFns + return $ readProcessStderrChan' e + +readProcessStderrChan' :: + (GmLines String -> IO ()) + -> FilePath -> [String] -> String -> IO String +readProcessStderrChan' pute = go pute where go :: (GmLines String -> IO ()) -> FilePath -> [String] -> String -> IO String go putErr exe args input = do diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 1b75940..c0fd19b 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -32,11 +32,11 @@ import System.Directory import System.FilePath import System.Process import System.Info.Extra -import System.Exit import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Caching +import Language.Haskell.GhcMod.Output import qualified Language.Haskell.GhcMod.Utils as U import Utils (mightExist) import Prelude @@ -77,21 +77,21 @@ findCabalFile dir = do findStackConfigFile :: FilePath -> IO (Maybe FilePath) findStackConfigFile dir = mightExist (dir "stack.yaml") -getStackDistDir :: FilePath -> IO (Maybe FilePath) -getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do - takeWhile (/='\n') <$> readStack ["path", "--dist-dir"] +getStackDistDir :: OutputOpts -> FilePath -> IO (Maybe FilePath) +getStackDistDir oopts projdir = U.withDirectory_ projdir $ runMaybeT $ do + takeWhile (/='\n') <$> readStack oopts ["path", "--dist-dir"] -getStackGhcPath :: FilePath -> IO (Maybe FilePath) -getStackGhcPath = findExecutablesInStackBinPath "ghc" +getStackGhcPath :: OutputOpts -> FilePath -> IO (Maybe FilePath) +getStackGhcPath oopts = findExecutablesInStackBinPath oopts "ghc" -getStackGhcPkgPath :: FilePath -> IO (Maybe FilePath) -getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" +getStackGhcPkgPath :: OutputOpts -> FilePath -> IO (Maybe FilePath) +getStackGhcPkgPath oopts = findExecutablesInStackBinPath oopts "ghc-pkg" -findExecutablesInStackBinPath :: String -> FilePath -> IO (Maybe FilePath) -findExecutablesInStackBinPath exe projdir = +findExecutablesInStackBinPath :: OutputOpts -> String -> FilePath -> IO (Maybe FilePath) +findExecutablesInStackBinPath oopts exe projdir = U.withDirectory_ projdir $ runMaybeT $ do path <- splitSearchPath . takeWhile (/='\n') - <$> readStack ["path", "--bin-path"] + <$> readStack oopts ["path", "--bin-path"] MaybeT $ listToMaybe <$> findExecutablesInDirectories' path exe findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] @@ -103,13 +103,11 @@ findExecutablesInDirectories' path binary = exeExtension = if isWindows then "exe" else "" -readStack :: [String] -> MaybeT IO String -readStack args = do +readStack :: OutputOpts -> [String] -> MaybeT IO String +readStack oopts args = do stack <- MaybeT $ findExecutable "stack" - (e, out, err) <- liftIO $ readProcessWithExitCode stack args "" - case e of - ExitSuccess -> return out - (ExitFailure rv) -> throw $ GMEStackBootrap rv err + liftIO $ flip catch (\(e :: IOError) -> throw $ GMEStackBootrap $ show e) $ do + evaluate =<< gmUnsafeReadProcess oopts stack args "" -- | Get path to sandbox config file getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index ff893fb..135e157 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -88,15 +88,19 @@ data Programs = Programs { , stackProgram :: FilePath } deriving (Show) -data Options = Options { - outputStyle :: OutputStyle +data OutputOpts = OutputOpts { + -- | Verbosity + logLevel :: GmLogLevel + , outputStyle :: OutputStyle -- | Line separator string. , lineSeparator :: LineSeparator -- | Stdout/err line multiplexing using prefix encoding. @fst@ is stdout, -- @snd@ is stderr prefix. , linePrefix :: Maybe (String, String) - -- | Verbosity - , logLevel :: GmLogLevel + } deriving (Show) + +data Options = Options { + outputOpts :: OutputOpts , programs :: Programs -- | GHC command line options set on the @ghc-mod@ command line , ghcUserOptions:: [GHCOption] @@ -113,10 +117,12 @@ data Options = Options { -- | A default 'Options'. defaultOptions :: Options defaultOptions = Options { - outputStyle = PlainStyle - , lineSeparator = LineSeparator "\0" - , linePrefix = Nothing - , logLevel = GmWarning + outputOpts = OutputOpts { + outputStyle = PlainStyle + , lineSeparator = LineSeparator "\0" + , linePrefix = Nothing + , logLevel = GmWarning + } , programs = Programs { ghcProgram = "ghc" , ghcPkgProgram = "ghc-pkg" @@ -379,7 +385,7 @@ data GhcModError | GMECabalStateFile GMConfigStateFileError -- ^ Reading Cabal's state configuration file falied somehow. - | GMEStackBootrap Int String + | GMEStackBootrap String -- ^ Bootstrapping @stack@ environment failed (process exited with failure) deriving (Eq,Show,Typeable) @@ -409,4 +415,5 @@ instance Serialize ChEntrypoint mkLabel ''GhcModCaches mkLabel ''GhcModState mkLabel ''Options +mkLabel ''OutputOpts mkLabel ''Programs diff --git a/src/GHCMod.hs b/src/GHCMod.hs index f27fc03..4d2af4c 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -249,28 +249,29 @@ intToLogLevel = toEnum globalArgSpec :: [OptDescr (Options -> Either [String] Options)] globalArgSpec = [ option "v" ["verbose"] "Increase or set log level. (0-7)" $ - optArg "LEVEL" $ \ml o -> Right $ o { - logLevel = case ml of - Nothing -> increaseLogLevel (logLevel o) - Just l -> toEnum $ min 7 $ read l - } + optArg "LEVEL" $ \ml o -> Right $ case ml of + Nothing -> + modify (lLogLevel . lOutputOpts) increaseLogLevel o + Just l -> + set (lLogLevel . lOutputOpts) (toEnum $ min 7 $ read l) o , option "s" [] "Be silent, set log level to 0" $ - NoArg $ \o -> Right $ o { logLevel = toEnum 0 } + NoArg $ \o -> Right $ set (lLogLevel . lOutputOpts) (toEnum 0) o , option "l" ["tolisp"] "Format output as an S-Expression" $ - NoArg $ \o -> Right $ o { outputStyle = LispStyle } + NoArg $ \o -> Right $ set (lOutputStyle . lOutputOpts) LispStyle o , option "b" ["boundary", "line-seperator"] "Output line separator"$ - reqArg "SEP" $ \s o -> Right $ o { lineSeparator = LineSeparator s } + reqArg "SEP" $ \s o -> Right $ set (lLineSeparator . lOutputOpts) (LineSeparator s) o + , option "" ["line-prefix"] "Output line separator"$ - reqArg "OUT,ERR" $ \s o -> let - [out, err] = splitOn "," s - in Right $ o { linePrefix = Just (out, err) } + reqArg "OUT,ERR" $ \s o -> let + [out, err] = splitOn "," s + in Right $ set (lLinePrefix . lOutputOpts) (Just (out, err)) o , option "g" ["ghcOpt", "ghc-option"] "Option to be passed to GHC" $ - reqArg "OPT" $ \g o -> Right $ - o { ghcUserOptions = g : ghcUserOptions o } + reqArg "OPT" $ \g o -> Right $ + o { ghcUserOptions = g : ghcUserOptions o } {- File map docs: @@ -307,34 +308,34 @@ Exposed functions: mapped. Works exactly the same as `unmap-file` interactive command -} , option "" ["map-file"] "Redirect one file to another, --map-file \"file1.hs=file2.hs\"" $ - reqArg "OPT" $ \g o -> - let m = case second (drop 1) $ span (/='=') g of - (s,"") -> (s, Nothing) - (f,t) -> (f, Just t) - in - Right $ o { fileMappings = m : fileMappings o } + reqArg "OPT" $ \g o -> + let m = case second (drop 1) $ span (/='=') g of + (s,"") -> (s, Nothing) + (f,t) -> (f, Just t) + in + Right $ o { fileMappings = m : fileMappings o } , option "" ["with-ghc"] "GHC executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lGhcProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lGhcProgram . lPrograms) p o , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $ - reqArg "PATH" $ \p o -> Right $ set (lGhcPkgProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lGhcPkgProgram . lPrograms) p o , option "" ["with-cabal"] "cabal-install executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lCabalProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lCabalProgram . lPrograms) p o , option "" ["with-stack"] "stack executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lStackProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lStackProgram . lPrograms) p o , option "" ["version"] "print version information" $ - NoArg $ \_ -> Left ["version"] + NoArg $ \_ -> Left ["version"] , option "" ["help"] "print this help message" $ - NoArg $ \_ -> Left ["help"] - + NoArg $ \_ -> Left ["help"] ] + parseGlobalArgs :: [String] -> Either InvalidCommandLine (Options, [String]) parseGlobalArgs argv = case O.getOpt' RequireOrder globalArgSpec argv of @@ -555,7 +556,7 @@ exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure exitError' :: Options -> String -> IO a exitError' opts msg = - gmUnsafeErrStrLn opts (dropWhileEnd (=='\n') msg) >> liftIO exitFailure + gmUnsafeErrStrLn (outputOpts opts) (dropWhileEnd (=='\n') msg) >> liftIO exitFailure fatalError :: String -> a fatalError s = throw $ FatalError $ "ghc-mod: " ++ s From 4aa75818d8f48179ac53c39585ba8d01fac9bbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 31 Aug 2015 08:01:20 +0200 Subject: [PATCH 088/147] Fix tests --- Language/Haskell/GhcMod/Convert.hs | 12 ++++++------ Language/Haskell/GhcMod/PathsAndFiles.hs | 3 ++- test/BrowseSpec.hs | 1 + test/CabalHelperSpec.hs | 1 + test/CradleSpec.hs | 7 ++++--- test/FileMappingSpec.hs | 6 +++--- test/FlagSpec.hs | 1 + test/LangSpec.hs | 1 + test/ListSpec.hs | 1 + test/TestUtils.hs | 12 +++++++++--- 10 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Language/Haskell/GhcMod/Convert.hs b/Language/Haskell/GhcMod/Convert.hs index a17abd0..480bd04 100644 --- a/Language/Haskell/GhcMod/Convert.hs +++ b/Language/Haskell/GhcMod/Convert.hs @@ -47,9 +47,9 @@ lineSep oopts = interpret lsep -- | -- --- >>> toLisp defaultOptions "fo\"o" "" +-- >>> toLisp (outputOpts defaultOptions) "fo\"o" "" -- "\"fo\\\"o\"" --- >>> toPlain defaultOptions "foo" "" +-- >>> toPlain (outputOpts defaultOptions) "foo" "" -- "foo" instance ToString String where toLisp oopts = quote oopts @@ -57,9 +57,9 @@ instance ToString String where -- | -- --- >>> toLisp defaultOptions ["foo", "bar", "ba\"z"] "" +-- >>> toLisp (outputOpts defaultOptions) ["foo", "bar", "ba\"z"] "" -- "(\"foo\" \"bar\" \"ba\\\"z\")" --- >>> toPlain defaultOptions ["foo", "bar", "baz"] "" +-- >>> toPlain (outputOpts defaultOptions) ["foo", "bar", "baz"] "" -- "foo\nbar\nbaz" instance ToString [String] where toLisp oopts = toSexp1 oopts @@ -72,9 +72,9 @@ instance ToString [ModuleString] where -- | -- -- >>> let inp = [((1,2,3,4),"foo"),((5,6,7,8),"bar")] :: [((Int,Int,Int,Int),String)] --- >>> toLisp defaultOptions inp "" +-- >>> toLisp (outputOpts defaultOptions) inp "" -- "((1 2 3 4 \"foo\") (5 6 7 8 \"bar\"))" --- >>> toPlain defaultOptions inp "" +-- >>> toPlain (outputOpts defaultOptions) inp "" -- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\"" instance ToString [((Int,Int,Int,Int),String)] where toLisp oopts = toSexp2 . map toS diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index c0fd19b..42557f4 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -21,6 +21,7 @@ module Language.Haskell.GhcMod.PathsAndFiles ( import Config (cProjectVersion) import Control.Applicative +import Control.Exception as E import Control.Monad import Control.Monad.Trans.Maybe import Data.List @@ -106,7 +107,7 @@ findExecutablesInDirectories' path binary = readStack :: OutputOpts -> [String] -> MaybeT IO String readStack oopts args = do stack <- MaybeT $ findExecutable "stack" - liftIO $ flip catch (\(e :: IOError) -> throw $ GMEStackBootrap $ show e) $ do + liftIO $ flip E.catch (\(e :: IOError) -> throw $ GMEStackBootrap $ show e) $ do evaluate =<< gmUnsafeReadProcess oopts stack args "" -- | Get path to sandbox config file diff --git a/test/BrowseSpec.hs b/test/BrowseSpec.hs index d615d50..657f423 100644 --- a/test/BrowseSpec.hs +++ b/test/BrowseSpec.hs @@ -3,6 +3,7 @@ module BrowseSpec where import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec +import Prelude import TestUtils import Dir diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 231fc3c..3c2aa4e 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -10,6 +10,7 @@ import Test.Hspec import System.Directory import System.FilePath import System.Process (readProcess, system) +import Prelude import Dir import TestUtils diff --git a/test/CradleSpec.hs b/test/CradleSpec.hs index 360b7e0..642e51d 100644 --- a/test/CradleSpec.hs +++ b/test/CradleSpec.hs @@ -7,6 +7,7 @@ import Language.Haskell.GhcMod.Types import System.Directory (canonicalizePath) import System.FilePath (pathSeparator) import Test.Hspec +import Prelude import Dir @@ -35,14 +36,14 @@ spec = do it "returns the current directory" $ do withDirectory_ "/" $ do curDir <- stripLastDot <$> canonicalizePath "/" - res <- clean_ findCradle + res <- clean_ $ findCradle (outputOpts defaultOptions) cradleCurrentDir res `shouldBe` curDir cradleRootDir res `shouldBe` curDir cradleCabalFile res `shouldBe` Nothing it "finds a cabal file and a sandbox" $ do withDirectory "test/data/cabal-project/subdir1/subdir2" $ \dir -> do - res <- relativeCradle dir <$> clean_ findCradle + res <- relativeCradle dir <$> clean_ (findCradle (outputOpts defaultOptions)) cradleCurrentDir res `shouldBe` "test/data/cabal-project/subdir1/subdir2" @@ -54,7 +55,7 @@ spec = do it "works even if a sandbox config file is broken" $ do withDirectory "test/data/broken-sandbox" $ \dir -> do - res <- relativeCradle dir <$> clean_ findCradle + res <- relativeCradle dir <$> clean_ (findCradle (outputOpts defaultOptions)) cradleCurrentDir res `shouldBe` "test" "data" "broken-sandbox" diff --git a/test/FileMappingSpec.hs b/test/FileMappingSpec.hs index 8cfe2b0..f3c0a3d 100644 --- a/test/FileMappingSpec.hs +++ b/test/FileMappingSpec.hs @@ -114,7 +114,7 @@ spec = do it "should work even if file doesn't exist" $ do withDirectory_ "test/data/file-mapping" $ do let fm = [("Nonexistent.hs", "main = putStrLn \"Hello World!\"\n")] - res <- run defaultOptions{logLevel=GmDebug} $ do + res <- run defaultOptions $ do mapM_ (uncurry loadMappedFileSource) fm checkSyntax ["Nonexistent.hs"] res `shouldBe` "Nonexistent.hs:1:1:Warning: Top-level binding with no type signature: main :: IO ()\n" @@ -224,7 +224,7 @@ spec = do writeFile (tmpdir "Bar_Redir.hs") srcBar let fm = [("Foo.hs", tmpdir "Foo_Redir.hs") ,("Bar.hs", tmpdir "Bar_Redir.hs")] - res <- run defaultOptions{logLevel = GmDebug} $ do + res <- run defaultOptions $ do mapM_ (uncurry loadMappedFile) fm types "Bar.hs" 5 1 res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] @@ -234,7 +234,7 @@ spec = do withDirectory_ "test/data/file-mapping" $ do let fm = [("Foo.hs", srcFoo) ,("Bar.hs", srcBar)] - res <- run defaultOptions{logLevel = GmDebug} $ do + res <- run defaultOptions $ do mapM_ (uncurry loadMappedFileSource) fm types "Bar.hs" 5 1 res `shouldBe` unlines ["5 1 5 20 \"[Char]\""] diff --git a/test/FlagSpec.hs b/test/FlagSpec.hs index 80fc893..af5438d 100644 --- a/test/FlagSpec.hs +++ b/test/FlagSpec.hs @@ -4,6 +4,7 @@ import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec import TestUtils +import Prelude spec :: Spec spec = do diff --git a/test/LangSpec.hs b/test/LangSpec.hs index 7c624cc..617ede5 100644 --- a/test/LangSpec.hs +++ b/test/LangSpec.hs @@ -4,6 +4,7 @@ import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec import TestUtils +import Prelude spec :: Spec spec = do diff --git a/test/ListSpec.hs b/test/ListSpec.hs index 9fc4648..828b08e 100644 --- a/test/ListSpec.hs +++ b/test/ListSpec.hs @@ -4,6 +4,7 @@ import Control.Applicative import Language.Haskell.GhcMod import Test.Hspec import TestUtils +import Prelude spec :: Spec spec = do diff --git a/test/TestUtils.hs b/test/TestUtils.hs index dfe0644..5f69d05 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -18,14 +18,17 @@ import Language.Haskell.GhcMod.Cradle import Language.Haskell.GhcMod.Types import Control.Arrow +import Control.Category import Control.Applicative import Control.Monad.Error (ErrorT, runErrorT) import Control.Monad.Trans.Journal import Data.List.Split +import Data.Label import Data.String import System.FilePath import System.Directory import Test.Hspec +import Prelude hiding ((.)) import Exception @@ -56,7 +59,7 @@ runGhcModTSpec' :: IOish m runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> withGhcModEnvSpec dir' opt $ \env -> do first (fst <$>) <$> runGhcModT'' env defaultGhcModState - (gmSetLogLevel (logLevel opt) >> action) + (gmSetLogLevel (logLevel $ outputOpts opt) >> action) -- | Run GhcMod run :: Options -> GhcModT IO a -> IO a @@ -65,11 +68,14 @@ run opt a = extract $ runGhcModTSpec opt a -- | Run GhcMod with default options runD :: GhcModT IO a -> IO a runD = - extract . runGhcModTSpec defaultOptions { logLevel = testLogLevel } + extract . runGhcModTSpec (setLogLevel testLogLevel defaultOptions) runD' :: FilePath -> GhcModT IO a -> IO a runD' dir = - extract . runGhcModTSpec' dir defaultOptions { logLevel = testLogLevel } + extract . runGhcModTSpec' dir (setLogLevel testLogLevel defaultOptions) + +setLogLevel :: GmLogLevel -> Options -> Options +setLogLevel = set (lLogLevel . lOutputOpts) runE :: ErrorT e IO a -> IO (Either e a) runE = runErrorT From 899d583549468a2be47f282a87a04f065b0d3686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 31 Aug 2015 08:55:49 +0200 Subject: [PATCH 089/147] `error` -> throw $ GMEProcess --- Language/Haskell/GhcMod/Error.hs | 4 ++-- Language/Haskell/GhcMod/Output.hs | 8 +------- Language/Haskell/GhcMod/PathsAndFiles.hs | 1 - Language/Haskell/GhcMod/Types.hs | 4 ++-- test/InfoSpec.hs | 1 + 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Language/Haskell/GhcMod/Error.hs b/Language/Haskell/GhcMod/Error.hs index 2bb3f4e..21f5892 100644 --- a/Language/Haskell/GhcMod/Error.hs +++ b/Language/Haskell/GhcMod/Error.hs @@ -126,12 +126,12 @@ gmeDoc e = case e of compsDoc sc = fsep $ punctuate comma $ map gmComponentNameDoc $ Set.toList sc - GMEProcess cmd args emsg -> let c = showCommandForUser cmd args in + GMEProcess _fn cmd args emsg -> let c = showCommandForUser cmd args in case emsg of Right err -> text (printf "Launching system command `%s` failed: " c) <> gmeDoc err - Left (_out, _err, rv) -> text $ + Left rv -> text $ printf "Launching system command `%s` failed (exited with %d)" c rv GMENoCabalFile -> text "No cabal file found." diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 6115707..b51e8bb 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -188,7 +188,7 @@ readProcessStderrChan' pute = go pute res <- waitForProcess h case res of ExitFailure rv -> - processFailedException "readProcessStderrChan" exe args rv + throw $ GMEProcess "readProcessStderrChan" exe args $ Left rv ExitSuccess -> return output where @@ -204,9 +204,3 @@ withForkWait async body = do tid <- forkIO $ try (restore async) >>= putMVar waitVar let wait = takeMVar waitVar >>= either throwIO return restore (body wait) `onException` killThread tid - -processFailedException :: String -> String -> [String] -> Int -> IO a -processFailedException fn exe args rv = - error $ concat [ fn, ": ", exe, " " - , intercalate " " (map show args) - , " (exit " ++ show rv ++ ")"] diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 42557f4..808c932 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -35,7 +35,6 @@ import System.Process import System.Info.Extra import Language.Haskell.GhcMod.Types -import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Caching import Language.Haskell.GhcMod.Output import qualified Language.Haskell.GhcMod.Utils as U diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 135e157..8e95ceb 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -372,9 +372,9 @@ data GhcModError | GMECabalCompAssignment [(Either FilePath ModuleName, Set ChComponentName)] -- ^ Could not find a consistent component assignment for modules - | GMEProcess String [String] (Either (String, String, Int) GhcModError) + | GMEProcess String String [String] (Either Int GhcModError) -- ^ Launching an operating system process failed. Fields in - -- order: command, arguments, (stdout, stderr, exitcode) + -- order: function, command, arguments, (stdout, stderr, exitcode) | GMENoCabalFile -- ^ No cabal file found. diff --git a/test/InfoSpec.hs b/test/InfoSpec.hs index 6a5296c..bd689a7 100644 --- a/test/InfoSpec.hs +++ b/test/InfoSpec.hs @@ -12,6 +12,7 @@ import System.Environment (getExecutablePath) import System.FilePath import Test.Hspec import TestUtils +import Prelude spec :: Spec spec = do From b124fa7d0f77c8c76cffa3011f8c0383844f2243 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 27 Aug 2015 10:42:28 +0900 Subject: [PATCH 090/147] Using "^import +". (#570) --- elisp/ghc-command.el | 6 +++--- elisp/ghc-comp.el | 2 +- elisp/ghc-ins-mod.el | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/elisp/ghc-command.el b/elisp/ghc-command.el index 7970a12..df9a69f 100644 --- a/elisp/ghc-command.el +++ b/elisp/ghc-command.el @@ -53,7 +53,7 @@ (let ((inhibit-field-text-motion t)) (sort-subr nil 'forward-line 'end-of-line (lambda () - (re-search-forward "^import\\( *qualified\\)? *" nil t) + (re-search-forward "^import +\\(qualified\\)? *" nil t) nil) 'end-of-line)) (ghc-merge-lines)))) @@ -64,7 +64,7 @@ (while (not (eolp)) ;; qualified modlues are not merged at this moment. ;; fixme if it is improper. - (if (looking-at "^import *\\([A-Z][^ \n]+\\) *(\\(.*\\))$") + (if (looking-at "^import +\\([A-Z][^ \n]+\\) *(\\(.*\\))$") (let ((mod (match-string-no-properties 1)) (syms (match-string-no-properties 2)) (beg (point))) @@ -73,7 +73,7 @@ (forward-line))))) (defun ghc-merge-line (beg mod syms) - (let ((regex (concat "^import *" (regexp-quote mod) " *(\\(.*\\))$")) + (let ((regex (concat "^import +" (regexp-quote mod) " *(\\(.*\\))$")) duplicated) (while (looking-at regex) (setq duplicated t) diff --git a/elisp/ghc-comp.el b/elisp/ghc-comp.el index 2209ca2..20be9a1 100644 --- a/elisp/ghc-comp.el +++ b/elisp/ghc-comp.el @@ -265,7 +265,7 @@ unloaded modules are loaded") (let (ret) (save-excursion (goto-char (point-min)) - (while (re-search-forward "^import\\( *qualified\\)? +\\([^\n ]+\\)" nil t) + (while (re-search-forward "^import +\\(qualified\\)? *\\([^\n ]+\\)" nil t) (ghc-add ret (match-string-no-properties 2)) (forward-line))) ret)) diff --git a/elisp/ghc-ins-mod.el b/elisp/ghc-ins-mod.el index 14db867..d567ec2 100644 --- a/elisp/ghc-ins-mod.el +++ b/elisp/ghc-ins-mod.el @@ -56,7 +56,7 @@ (defun ghc-goto-module-position () (goto-char (point-max)) - (if (re-search-backward "^import" nil t) + (if (re-search-backward "^import +" nil t) (ghc-goto-empty-line) (if (not (re-search-backward "^module" nil t)) (goto-char (point-min)) From d681130c887024c218bdfa2e43d2f87fdd56f488 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Fri, 28 Aug 2015 10:23:57 +0900 Subject: [PATCH 091/147] ghc-debug now displays the output of "ghc-mod debug". (#571) --- elisp/ghc.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/elisp/ghc.el b/elisp/ghc.el index bdad76c..0c4f967 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -138,7 +138,8 @@ (el-ver ghc-version) (ghc-ver (ghc-run-ghc-mod '("--version") "ghc")) (ghc-mod-ver (ghc-run-ghc-mod '("version"))) - (path (getenv "PATH"))) + (path (getenv "PATH")) + (debug (ghc-run-ghc-mod '("debug")))) ;; before switching buffers. (switch-to-buffer (get-buffer-create "**GHC Debug**")) (erase-buffer) (insert "Path: check if you are using intended programs.\n") @@ -150,7 +151,10 @@ (insert (format "\t %s\n" ghc-mod-ver)) (insert (format "\t%s\n" ghc-ver)) (insert "\nEnvironment variables:\n") - (insert (format "\tPATH=%s\n" path)))) + (insert (format "\tPATH=%s\n" path)) + (insert "\nThe result of \"ghc-mod debug\":\n") + (insert debug) + (goto-char (point-min)))) (defun ghc-insert-template-or-signature (&optional flag) (interactive "P") From 30d95c5fcec534f90bab67a75f04b252112fc57a Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Fri, 28 Aug 2015 14:16:14 +0200 Subject: [PATCH 092/147] README.md: update the instructions for Nix & NixOS The stackoverflow article the README used to link to is outdated and no longer applies. --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0752888..28b1b3e 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ package is called `ghc` there, not `ghc-mod`) and install the ### Nix & NixOS -The installation is a little more involved in this environment as Nix needs some -ugly hacks to get packages using the GHC API to work, please refer to this -stackoverflow answer: - -http://stackoverflow.com/a/24228830 +`ghc-mod` works fine for users of Nix who follow a recent version of the +package database such as the `nixos-15.09` or `nixos-unstable` channel. Just +include the package `ghc-mod` into your `ghcWithPackages` environment like any +other library. The [Nixpkgs Haskell User's +Guide](http://hydra.nixos.org/job/nixpkgs/trunk/manual/latest/download-by-type/doc/manual#users-guide-to-the-haskell-infrastructure) +covers this subject in gret detail. ## Using the development version From 99ed6a94f1afc2473cc326ca6df39a4c1c69d765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 31 Aug 2015 03:39:47 +0200 Subject: [PATCH 093/147] Add IIJLAB presentation --- doc/presentation/Rokkitt.otf | Bin 0 -> 44508 bytes doc/presentation/SIL Open Font License.txt | 44 +++++ doc/presentation/architecture.pdf | Bin 0 -> 5746 bytes doc/presentation/architecture.tex | 44 +++++ doc/presentation/auto/main.el | 14 ++ doc/presentation/current-architecture.dia | Bin 0 -> 1701 bytes doc/presentation/current-architecture.png | Bin 0 -> 18358 bytes doc/presentation/gh-stars.png | Bin 0 -> 10524 bytes doc/presentation/hackage-dls.png | Bin 0 -> 11916 bytes doc/presentation/logo.pdf | Bin 0 -> 2067 bytes doc/presentation/main.pdf | Bin 0 -> 121827 bytes doc/presentation/main.tex | 204 +++++++++++++++++++++ doc/presentation/planned-architecture.png | Bin 0 -> 20532 bytes 13 files changed, 306 insertions(+) create mode 100644 doc/presentation/Rokkitt.otf create mode 100644 doc/presentation/SIL Open Font License.txt create mode 100644 doc/presentation/architecture.pdf create mode 100644 doc/presentation/architecture.tex create mode 100644 doc/presentation/auto/main.el create mode 100644 doc/presentation/current-architecture.dia create mode 100644 doc/presentation/current-architecture.png create mode 100644 doc/presentation/gh-stars.png create mode 100644 doc/presentation/hackage-dls.png create mode 100644 doc/presentation/logo.pdf create mode 100644 doc/presentation/main.pdf create mode 100644 doc/presentation/main.tex create mode 100644 doc/presentation/planned-architecture.png diff --git a/doc/presentation/Rokkitt.otf b/doc/presentation/Rokkitt.otf new file mode 100644 index 0000000000000000000000000000000000000000..64a20c00bfc0447ff9e38c057a669f4450983fe9 GIT binary patch literal 44508 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 p<}UO1e=*LP0=4Vi+lj0i>i+B!(E2 zl=x_nzTda*UF)24Pu%<8^T)gPUhmrbx7T_efd^XJ5hE zWq`JaHv%3A(Drsjz_sAcelBo;0o>OO;m$2D4OCSn3ql0K9ev0`upN2BzVlMl(6j5L zb3D{4MX_2Y6e+9r*rS8I!99PViU?~Mg|Vc)rhYY;v&Q^lclHHB;rZDi&4p$>h%+94 zW#agVX%{22lPfnB_hvQ)^5b&*W{n7-XM#9Rzo-jHv5aVwR7J>&6g%}q%>Go><}Hi$ z(xh*AXtlolYmaX7Ny+?Ev~RmAhH|Jmh%)@-si7R=NqX~ro50kK;fagRgG*TOwAjb@ zG9_6OLrfM$&+)Ya&*c>>N`O1>k3?sw#aUvGW=|}IUl*j#2j+W_3<0qjHCcuH?S6pNG!$)Oqd}+%{N7>{UG(-!oc^ zKIZ_-V7T-T+q`@el(wKv18~9iuZ%Qu&4@P+}1Yc50wsz zZL@HKx9~bnNyyMSUqB#TBrV4aR-x51cCW<6D;n&flBer!nZWsCV-pWX=4vbt7j)<~ zy2k^#{K$`yxN5R1Ji=X^=$8c|PlnB8HqIP%BXR3;`WTyLCd=Dem7T04JgGNqJCiDd z`@^Rl$qP*4E!NesH{HAKpY4}QsTc}f?LHm6&qtRu3JzAYHM7^tx>G49fQsdj&3s4q%DD8G+GmMDE~>{1m3n zlLdpO4^C9x8IC=5Urw`)SY6*T6KG5KudZaRbBenLw|O&GxmaYR>W$nvos|w0RV<-Q zPf#toT8M4%JfqN`luxwhf?2h%s~DuzFcxkZM_X-wV1v;$Pl%fZ-JHn3_pNMV`6%fy z2eKc{@*6qI<>?jvHtyfG=X0H&d+!!6zCY=>3R6i={EPi`8*kEaKb}`6p41p=Q4(4c8x1U|yIAu0YLpNHoYO)x(7}D*M zAxaGHXZtX;TG$KE$FgdDmc8@`v;6u46Ve*Jvrs*;k!5GWV|&+1Hq%ax!Tp{!v`TM+ zmC*L&ZWv+NES>PLEOKc}+GBhy(oVf@LIrBgl(4J*JIwrE*W)L#^8s^7%f-x zm}zcOjY9BN&Uk_Vhv36*v{8|N<)mh+;`UTbO8oG|H^j>WfY@o9qvvDEtKOF90V*#b z3ho!S=3YA`N#pm5fc4@WiF zGSrOTva`0lvetqow}GxEunOx=`*P6rsi$BlnPhh_oIXKJX%tI-%==mm{p76dwc(4w zY06ZMx6d^0+empXPy+g0mhxQq^O?YYg0!iM%_D2+mZ_7g1#*>&8;=WNm|9#Q26wh7 z)}yQf%+vuLb(}TF#l}h6U>uXR8A_@4?LR^@-5>P3F9K!`(Z3;hfOt2lB8G#HBmkx( zw92HH-OotCILhI8QhecxyyrI7u_RjLkAvT!&6Sg+eb9ONjLlguO`RpMhk(veb#|CJ zd9;ty(U~{>c0)qgj;{I=P?w=go~@?bmm3k8HZ^<3qKm+djXRfwe;i`}1&M zKxICKt!IO>HU}VT+_JOVvh!g!&xqjghR4+5BdAG=w0f;>#&eOTFO-UL7DC2To)jLR z&N~n5-?%PA~IXw9iK-Or)WB*Y{ zaz=#ykiSE9R{{d*;F+{6H9Xd*_C`bwxoImx&TbiN7C!>LCoh7lr@bj`SE|RShUq}o zq3s%6*&n9PI|oBbwX2gJI`x1_UvZhbgfeke}%P3Bf-0y@6F+d79 zi#!r7?|bar>hw0D2Tb?+mt=*IMN)*29%ID%0Wyzr{?hKJy!>xNr8+O4omTBB^z%*5RY zMcElD?@{^`0c*8TTfU<&nP~9tv4h{IlEU4DMxVs8_Hzt#;r?=mYQ^Tn-GwmY5kP#l zOCwR2nY*MIP^NS1?2vBr9%B^Y%dRpx+ln(g6#r>Gs+`uGGk2*V;7w^`LsfQ zZO7JTnm?=IQ@Cmt&*6=-aI0d-`}r@me1o|U)?X2g^n1rEH<&VbB$V^fDN$|D9^-v$ z#j6?*Lj`60*m-`h=xw-7IIlvJBwHRj((obtc{kN0lID^8l~)>c`m08eZsx03uj%iQ zZSDrxMub@e<5QfO)Wd$(<6XM{PIqU`x)^29-a5e7QQK1&_H~@;ekE3o9?|UkSk(nQ_~9hItI)W2rjOZFIzNyK2J9EyZu=3P zyIS$tw&i`gQmv7JeX{D{M{t5K9S5BgZk<={M?8N&XcCCrDCZ|Oye3ZH6jhqivb~rJ zz8Y&jKh5hv3D#73bckc?29#!S|G1YryI^7ze4%fozLe|S;t zJf4+)8n68%>XxhY!KsQugeVskpNs$3!(!?t8ATzHOeBod#1r75@@T>Evq&+{=e@K! zBYKsPB2TR{QDkH`lSU((^O22KV+0e-MU3DX@4a!cJ5U92MO_zU$-U~DrYf>>RPEjf z?pJytjTPA2IT{?PluIc4%PB9!RwIm%qkZi-&ijQ;>sKeV)z)T=;n?Y^x0{}?NdTcf z)=j*%=}wr&ug_5$CS7Ty{uw_*G~@3UbxN+A{s?3_Q^aJu6PYW-31-;$zDaie(n8zR zuFIdFsy^ad)+@UY^q;K#)&=ScY)j-0vISgnpz^bMvI+BlleXN^?K+T&isL(OA!e8v z1f2WgrNbKE4EOXr_$j-b(_vuhtGDeM<}p>?%RY_Ir8CGCg+m%@y`zbh(!>8LyXn!R zz}tJ5*s7%{1aX~RZMoIcEG2yQL^j&FVwriv;nVy<_K#amRcV8?eQ@*&A%T320}Uyz zPh$G3D4Th}wUZBJ&|1#0C*WmZtZpF*2=SBHD^#+Y-&(w}Us;q-4)Z=w9i1sRwoTfw zfg2EPP)}zn z=^<>x50-D{m%;$JBZ*D zY-7=tA8;yj4XZ@SEQfGsBf^#v7xYEfgzgrFyzicK{3)03=G$}*gEVg^T@j07Ym{ow zqNcXc9%PKsmd|&yY;gH_rufYlcPu_X?V#GJJ5RwKAxH~=JJ>2!oFVM4TB~wAZV}BAw^>Q{i!)l^U_&SJ~Q36N>}i|lru z+%S~X&<+g}Hkwq-=)bFHvZSZZp>72E9@=n3f4x@LcbCa2?}S~!IKFs6a80@2I6GKO zsWgU8vm}UR^G84F<(@w;>c{l}vho&MpM!euQ7;O#)H4`)bWyQbW(F9l83&UnF{+gSlDu zibKBZA4-}8`Z~~{gM}J{@@|((;WtNW0gTD6)N^lIY8%?y%i>Vh$i`MEYY<|>se&h9 z2h-`VkA%xbU1~Siw^tKEN5d2~b4#rDAE#~e z#Ken}3p@j{wTX<3ko{tvE8m``LKXuhyz1qtCDYy7EEf{A5S5nb{Fyv!+bL%&bFdRj zeww>F{M$pZoZ1_tz_`W%~qCedz5HJS#N5qepdRYs-D_8~WGZu&{RZ#Lpnd*$Y0u zRq)jgF%?6JTYlMisY^-tWl^J$7PR*KYH6FK)*jwZ*Cy%ByWZ5Dcu!IZI&sEH{l~A^ zj0Li}(G&Yol#JY2@$Hq@*7{Fl#?B_EZ%Op`U4>@0HrcU6r&2<#KjQ7emOt=t`?@t-Mr#XRXpUwQNtF2$= zv#s5JRuqn2da|d=K2yR5S$gKh1zCO?&xwno_#+E*mz7ogo1UI|-$SDCchJ7=XGm?H zEFs9s1eY|^_?4ErZkOC3+Yj|f8E2ciy>AWeb&0W5PWNvv^?h?^R2*Yy$7kC+Cg>_Fe{Uasxg~rJ-2o| z*ntV&eTH$L%t6r2=-V(;PZjcr3C#Y29_%5(qC?F^eOAPsaIO;7g-1YD_&f~OO-A_p zY@Mg}ZN<}aZeQX4$twx}{f7m{!_BMQ_UGb{ItLRf9Khq-DxyXnk@7Ka&mK}*lv1cBK+LfFjw+0(%YH8-yp!bv; z^PyR0ZSPw#f{n388GkSaAM-V!sy#EZ)-~g*bH-4A|GT4>T{* zAiBR-R`@1W%dboA=c8ad@nmeI8#26Y+}AV|yAA$97%ve}+|PNz%zjlR_`bF}P$w_3 z$dR>z1f3q+D zKKzje{k;SAa&T~y2Bz!0R89M3V$p8mlXmSJ9(NSd;mt=K&d}X zQD6{)TlO#U?-D0jS@7R?O*NhWGjXDbz*+A5kS3Wi>37mYp9xw`f7rI*pR?{diSa_$ zB73F1D|Buma|&F=>hb6tOzG2wvz}8RW1=%+NHit})CtM>b&R9$7!z^Rhp69W{doT| zDc-%V=Q{c=?JlNh;AoYR3_pPVg|}~2+So?4)VuV7om$`=SzF%X=(>06Wtv2j2lAVp zK^RJ>f8`#cPO@mbJcQRD8$4^~9bX5JA0E%$6f&Pr-CYae6oCR4v* zniMl7OZ6eF{V9I43(iWaP2@=Sls$gY$*!CZonAwZD`)+(GU^fI;g*l5yv)kgweu-# z2Dz$He6(+~MJ`&zjI}wkjURO=#=Ho=XmuBUz zkK|a()!_MnvCG()RxvmqZ|<8$VW)5ux^_a-@l7*7Izt zb&yG>8b;uj{SoSR9eCv5VOJQ@VVmz`O{!|`gqnYs8dpc<^L!q9;?4dGI`??g(LeXK z5PQR(VVa>8Lk!f2{pF&g4&eI$kvDBm6J<5ex+M#2*K*e@Il-e_}= zTA4F{ZDq`J_DZGkGBbf5_Rs**ot}_%!8-i2Y`~_2@eIDy@u$@f0;6m}CY;YRQ{3O> zyiuT))|rhK7DrcC&2sz8;@&~YRnJwtCj#tVF#^u#zg{h}a>-o2@IAnNM<;}~s^?qO z6%&aHnV`2Z{1GLakXDwC+^2|9Rr6P05{XX-tn6!*%cxU0AdBhK*sJ$?8hp7wnEL=1 z^M*eEy!JrZ|F!pzOyI5n6F)x$_g{IB2|(A^)sI{HzoiyTSz6u^F6Ar(mX(&1kpVi( zNlSr2U^yos7zmdJ%7Eai|Mv@9fT>3qoD3)ju!I5sEG=#OpSb!5tehPje%}@+BD0YU zTJ_KF3Q~99<(*Y06yhU*5D+BvbqVKCIRa&*pQt@zPx+uq&B#_tE%zYNot=Q-&aN(D d&Od)Y2;mrr2nmF{l7T^TQe*-GP-AVf{{mX=y4L^z literal 0 HcmV?d00001 diff --git a/doc/presentation/architecture.tex b/doc/presentation/architecture.tex new file mode 100644 index 0000000..ef85434 --- /dev/null +++ b/doc/presentation/architecture.tex @@ -0,0 +1,44 @@ +\documentclass{article} +\usepackage{polyglossia} +\usepackage{xcolor} +\usepackage{fontspec} +\usepackage{tikz} + +\begin{document} + + % \begin{tikzpicture} + % \draw (-1,0) -- (1,0); + % \draw (0,-1) -- (0,1); + + % \draw (-0.5,-0.5) rectangle (-1,-1); + % \end{tikzpicture}. + + \begin{tikzpicture}[every node/.style={draw}] + \matrix [draw=red,column sep=1cm] + { + \node {8}; & \node{1}; & \node {6}; \\ + \node {3}; & \node{5}; & \node {7}; \\ + \node {4}; & \node{9}; & \node {2}; \\ + }; + \end{tikzpicture} + + \begin{tikzpicture} + \matrix[draw=black,nodes=draw,column sep=1mm] at (0, 0) { + \node {check}; & + \node {type}; & + \node {browse}; & + \node {find}; & + \node {refine}; \\ + }; + + \matrix[draw=black,nodes=draw,column sep=1mm] at (0, 0) { + \node {check}; & + \node {type}; & + \node {browse}; & + \node {find}; & + \node {refine}; \\ + }; + \end{tikzpicture} + + +\end{document} \ No newline at end of file diff --git a/doc/presentation/auto/main.el b/doc/presentation/auto/main.el new file mode 100644 index 0000000..7544b2d --- /dev/null +++ b/doc/presentation/auto/main.el @@ -0,0 +1,14 @@ +(TeX-add-style-hook + "main" + (lambda () + (TeX-run-style-hooks + "latex2e" + "beamer" + "beamer10" + "polyglossia" + "xcolor" + "fontspec") + (TeX-add-symbols + "gm" + "gms"))) + diff --git a/doc/presentation/current-architecture.dia b/doc/presentation/current-architecture.dia new file mode 100644 index 0000000000000000000000000000000000000000..12f369239f167fc93397474036e3dc772c2f757e GIT binary patch literal 1701 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 E7kybrMgRZ+ literal 0 HcmV?d00001 diff --git a/doc/presentation/gh-stars.png b/doc/presentation/gh-stars.png new file mode 100644 index 0000000000000000000000000000000000000000..51ca361ad275bbb5b37b3fe3bdc0d61d91571f6c GIT binary patch literal 10524 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
ft{#scx@$O!shSrDHKXr^G#DT~q|D z_H=^mhLQkeBtMFA$-E1!>l;&AQYXb>?V+IKl-S5eLYTX zTl^?a^Jc+1lfzPD~wuBI;^zcafD;WhSsYJ>mk>KaLzx;H!KW}-q$SE9v@_niCXrRV>RN=T0R$*dg*V- z7*WG!`ddS%wRB;&Db^a55; zsUQb3e*5@W2REpN(QWsS&r|DCR%mI=t))SsWRjY0Jo{QhzB-h^OEw>)ZHz}97M_cE zHr_e0y2ewVxvpHg914?u_4MtAC?xf@td7VxHD}k#uRGbPUj-kWJn!_%QpxRL( z7BFZ=x$-Ovy)uWbU;1V0j5EU2ri8=^pzYIfuQkNc&uq z4x^isb5Ox^If6ABqbJRoJ1VVyw2`%_Z)$3SpixCpfQ{pbC0= zqoONtV_hFEKL_2F{46yi;J%R)@whpNJmHh82X^m&5}TWmH@iJ4+WUco;Gk0Pz|&o8 z8KkOasmfQA8}dz+KHS(#B~0u`(uns_)jiIuBJqygbz-NOWs~ROoDb~ z#KA^?SV?oW++8?COT$iBZ#pJ-wq|*zjT8a+aPnM{rkEW6=|oy#4i#ba5*}zznf^+4 zDftF2j9K^A|6NXcf}Q$KZ-E6%C{7KV_!ma5$6FN ztKm7J9Qs=%cWNB9uB;w$SBHTgp6$voI?KhAQcHpe+IP5s;OIlv*DF>;7dx}=$}9HB z&_-s{gSzdoCL6OMyCoK5Jg*}Bva4m4Rv8-i$3*=CvGzZ{Bw{`&-!p&A@c zv^crNvo-YDwWamoQdeX?vd9Vh4Y`zD5>z2CEGbFWYFziuG>wPu1Nojuj6ilqCVlppv=#@Qqz2TwWW zLZ>+`jy`^ct%_)KlY8~80fVxlf9Q7xubeWCY{Yqu96&jhp-tC>@+TinGcypKQaEMB z6LD=T`veu0Qd2|vrD~;$`4uYqhjJNel>JcbeW`zVvmG_WgB^>AWAkxzEm!|Tz2yw; zG9e4xagBsxXg{roikqek4wHh0CZ0Qyp`p7;U3KkeNC{MpZfq{-1+Y6rPpzQ+p8>po zV|D+9>irKK-~S)L`+sqK6#@ST+V`LI-oFUJ|AYXAf^YqEbd}0vCS?x)!5(EK6vfLw H8U_9@-)KQp literal 0 HcmV?d00001 diff --git a/doc/presentation/hackage-dls.png b/doc/presentation/hackage-dls.png new file mode 100644 index 0000000000000000000000000000000000000000..cc190f72542a1225807b030065c8e7c32feaa87c GIT binary patch literal 11916 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 z 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 Ua%E-;F)}bSF*gb&B}Gq03W;p{`~Uy| literal 0 HcmV?d00001 diff --git a/doc/presentation/main.tex b/doc/presentation/main.tex new file mode 100644 index 0000000..2f0d4f8 --- /dev/null +++ b/doc/presentation/main.tex @@ -0,0 +1,204 @@ +\documentclass{beamer} + +\usepackage{polyglossia} +\usepackage{xcolor} +\usepackage{fontspec} + +\newfontfamily\Rokkitt{Rokkitt.otf} + +\newcommand\gm{{\Rokkitt ghc-mod}\ } +\newcommand\gms{{\Rokkitt ghc-mod's}\ } + +\mode +{ + \usetheme{Rochester} + \usecolortheme{default} +} + +\definecolor{beamer@blendedblue}{HTML}{545488} +\definecolor{gmgrey}{HTML}{F3F3FF} + +\setbeamercolor{normal text}{fg=black,bg=white} +\setbeamercolor{alerted text}{fg=red} +\setbeamercolor{example text}{fg=green!50!black} + +\setbeamercolor{structure}{fg=beamer@blendedblue} + +\setbeamercolor{background canvas}{parent=normal text} +\setbeamercolor{background}{parent=background canvas} + +\setbeamercolor{palette primary}{fg=gmgrey,bg=beamer@blendedblue} % changed this +\setbeamercolor{palette secondary}{use=structure,fg=structure.fg!100!green} % changed this +\setbeamercolor{palette tertiary}{use=structure,fg=structure.fg!100!green} % changed this + +\title{\gm} +\subtitle{Making Haskell development even more fun} + +\author{\includegraphics{logo} \\ \bigskip Daniel Gr\"ober \and Kazu Yamamoto \vspace{-1em} } + +\pgfdeclareimage[height=0.5cm]{logo}{logo} +\logo{\pgfuseimage{logo}} + +% Delete this, if you do not want the table of contents to pop up at +% the beginning of each subsection: +\AtBeginSubsection[] +{ + \begin{frame}{Outline} + \tableofcontents[currentsection,currentsubsection] + \end{frame} +} + +\begin{document} + +\begin{frame} + \titlepage +\end{frame} + +\begin{frame}{Outline} + \tableofcontents +\end{frame} + +\section{Motivation} + +\subsection{What is it?} + +\begin{frame}{What is \gm?} + First some marketing blurb: + + \begin{block}{} + \gm is a backend program for enhancing editors and other kinds of + development environments with support for Haskell, a library for abstracting + the black magic incantations required to use the API of the most popular + Haskell compiler in various build environments and an Emacs Lisp frontend + program to let users access it's features. + \end{block} +\end{frame} + +\begin{frame}{What does it do?} + \begin{itemize} + \item \texttt{check} modules for compilation errors and warnings, + \item get the inferred \texttt{type} of an expression in a module, + \item \texttt{list} modules, compiler and \texttt{lang}uage \texttt{flag}s, + \item \texttt{browse} symbols defined in modules, + \item \texttt{find} which module a symbol was defined in, + \item lookup \texttt{doc}umentation for a symbol or module + \item and a bunch of more obscure things. + \end{itemize} +\end{frame} + +\subsection{Why work on it?} + +\begin{frame}{Why?} + \begin{itemize} + \item It's actually rather popular: \vspace{1em} + + \item GitHub + \includegraphics[width=\textwidth]{gh-stars} + + \item Hackage (Haskell package repository) + \includegraphics[width=\textwidth]{hackage-dls} + + \item Also working with compilers is fun, right? + \end{itemize} +\end{frame} + + +\section{Implementation details} + +\subsection{Current architecture} +\begin{frame}{Current architecture} + \includegraphics[width=\textwidth]{current-architecture} +\end{frame} + +\begin{frame}{\gm the Elisp program} + \begin{itemize} + \item Extends haskell-mode to allow access to \gms features + \item There really isn't much more to it than that + \end{itemize} +\end{frame} + +\begin{frame}{\gm the program} + \begin{itemize} + \item Development environment communicates with \gm process + \item Exists as a one-shot and long running process version + \begin{itemize} + \item \gm simple, doesn't have to worry about caching + \item \gm ``interactive'' much more complex, needs to be very aware of + changing environment and how that affects compiler internal caches + \end{itemize} + \item interactive \gm is generally much faster than \gm at least for features + that require compilation though + \end{itemize} +\end{frame} + +\begin{frame}{\gm the library} + \begin{itemize} + \item \gm frontend programs use the library to implement all functionality + \item Frontends are very thin wrappers around the library, all the + intelligence is in there + \item Primary entry point abstracts away environment setup and just gives the + underlying tool a compiler session to work with + \item Right now it's only of limited use for implementing new \gm like tools + on top of it and definitely needs a redesign (for v6.0 probably) + \item Alan Zimmerman's Haskell Refactorer (HaRe) uses it for example + \end{itemize} +\end{frame} + +\begin{frame}{Problems} + \begin{itemize} + \item Extending \gm from the outside is hard to impossible + \item External tools end up depending on ghc-mod making it difficult for us to + make use of them + \item This all just leads to fragmentation in the already fragmented Haskell + Tooling Landscape + \item one tool, \texttt{mote}, just ended up copy-pasting part of \gms + environment support code straight into it's codebase \texttt{-.-} + \item development environments essentially need to support every tooling + project themselves + \end{itemize} +\end{frame} + +\subsection{Redesigned architecture} +\begin{frame}{Redesigned architecture} + \includegraphics[width=\textwidth]{planned-architecture} +\end{frame} + +\begin{frame}{Redesigned architecture} + \begin{itemize} + \item Factor out commands from library into a seperate package + \item Refine the library so any tool can actually make use of it + \item Design a communication library towards the development environment which + provides some common ground for tools and frontend developers + \end{itemize} +\end{frame} + +\section{The internship} + +\subsection{What we have done so far} + +\begin{frame}{Bitrot} + \begin{itemize} + \item Cabal version 1.22 completely broke \gms hack'y way of getting + information about the build system state + \item To fix this (recurring) problem once and for all we had to completely + re-design how we access Cabal's internal state + \item Next GHC version 7.10 came along and also broke \gm + \item Adding support for the new compiler version was easy + \item Cabal-1.22 support was however still blocking the release + \end{itemize} +\end{frame} + +\subsection{What is still to be done} + +\begin{frame}{TODO} + \begin{itemize} + \item Essentially implement all of the architectural changes + \item Support for implementing REPLs on top of ghc-mod + \item Speed up \gm program by adding network RPC support + \end{itemize} +\end{frame} + +\begin{frame}{Questions?} +\end{frame} + +\end{document} diff --git a/doc/presentation/planned-architecture.png b/doc/presentation/planned-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..46a800f42fedd8780ef119dee3b3ff831659894a GIT binary patch literal 20532 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 X>)5K4@_V3Wg!AIEV##MO|N6fGhzNqg literal 0 HcmV?d00001 From 2af1da960bd936baf244f2c04d5bac935444589b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 1 Sep 2015 04:14:15 +0200 Subject: [PATCH 094/147] Also catch exceptions thrown in IO --- Language/Haskell/GhcMod.hs | 4 ++-- Language/Haskell/GhcMod/Output.hs | 10 +++++----- src/GHCMod.hs | 14 +++++++++----- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index a6a555b..9ee6180 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -62,8 +62,8 @@ module Language.Haskell.GhcMod ( , gmErrStr , gmPutStrLn , gmErrStrLn - , gmUnsafePutStrLn - , gmUnsafeErrStrLn + , gmUnsafePutStr + , gmUnsafeErrStr -- * FileMapping , loadMappedFile , loadMappedFileSource diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index b51e8bb..92d7f3e 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -23,8 +23,8 @@ module Language.Haskell.GhcMod.Output ( , gmPutStrLn , gmErrStrLn , gmReadProcess - , gmUnsafePutStrLn - , gmUnsafeErrStrLn + , gmUnsafePutStr + , gmUnsafeErrStr , gmUnsafeReadProcess , stdoutGateway ) where @@ -109,10 +109,10 @@ gmErrStr str = do putErr $ toGmLines str -- | Only use these when you're sure there are no other writers on stdout -gmUnsafePutStrLn, gmUnsafeErrStrLn +gmUnsafePutStr, gmUnsafeErrStr :: MonadIO m => OutputOpts -> String -> m () -gmUnsafePutStrLn oopts = (fst $ outputFns' oopts GmOutputStdio) . toGmLines -gmUnsafeErrStrLn oopts = (snd $ outputFns' oopts GmOutputStdio) . toGmLines +gmUnsafePutStr oopts = (fst $ outputFns' oopts GmOutputStdio) . toGmLines +gmUnsafeErrStr oopts = (snd $ outputFns' oopts GmOutputStdio) . toGmLines gmUnsafeReadProcess :: OutputOpts -> FilePath -> [String] -> String -> IO String gmUnsafeReadProcess oopts = diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 4d2af4c..a550f32 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -25,7 +25,7 @@ import System.FilePath (()) import System.Directory (setCurrentDirectory, getAppUserDataDirectory, removeDirectoryRecursive) import System.Environment (getArgs) -import System.IO (stdout, hSetEncoding, utf8, hFlush) +import System.IO import System.Exit import Text.PrettyPrint import Prelude hiding ((.)) @@ -396,7 +396,10 @@ main = do args <- getArgs case parseGlobalArgs args of Left e -> throw e - Right res -> progMain res + Right res@(globalOptions,_) -> catches (progMain res) [ + Handler $ \(e :: GhcModError) -> + exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc e) + ] progMain :: (Options,[String]) -> IO () progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do @@ -407,7 +410,7 @@ progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ ghcCommands cmdArgs where hndle action = do - (e, _l) <- action + (e, _l) <- liftIO . evaluate =<< action case e of Right _ -> return () @@ -555,8 +558,9 @@ exitError :: IOish m => String -> GhcModT m a exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure exitError' :: Options -> String -> IO a -exitError' opts msg = - gmUnsafeErrStrLn (outputOpts opts) (dropWhileEnd (=='\n') msg) >> liftIO exitFailure +exitError' opts msg = do + gmUnsafeErrStr (outputOpts opts) msg + liftIO exitFailure fatalError :: String -> a fatalError s = throw $ FatalError $ "ghc-mod: " ++ s From 41de8b8b2e98cabc4e33a0c7d46a2033bb230745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 1 Sep 2015 10:27:12 +0200 Subject: [PATCH 095/147] Sandwich new Monad layer GmOutT into transformer stack This way we can have access to some options pre Cradle setup which should fix the output interleaving problems I was observing. --- Language/Haskell/GhcMod/Browse.hs | 8 +- Language/Haskell/GhcMod/CabalHelper.hs | 34 ++-- Language/Haskell/GhcMod/CaseSplit.hs | 5 +- Language/Haskell/GhcMod/Convert.hs | 8 +- Language/Haskell/GhcMod/Cradle.hs | 36 ++-- Language/Haskell/GhcMod/Debug.hs | 2 +- Language/Haskell/GhcMod/FillSig.hs | 12 +- Language/Haskell/GhcMod/HomeModuleGraph.hs | 6 +- Language/Haskell/GhcMod/Info.hs | 5 +- Language/Haskell/GhcMod/Lint.hs | 2 +- Language/Haskell/GhcMod/Logger.hs | 5 +- Language/Haskell/GhcMod/Logging.hs | 4 +- Language/Haskell/GhcMod/Modules.hs | 6 +- Language/Haskell/GhcMod/Monad.hs | 47 +++-- Language/Haskell/GhcMod/Monad/Types.hs | 226 ++++++++++++++------- Language/Haskell/GhcMod/Output.hs | 88 ++++---- Language/Haskell/GhcMod/PathsAndFiles.hs | 33 +-- Language/Haskell/GhcMod/Target.hs | 16 +- Language/Haskell/GhcMod/Types.hs | 59 +++--- src/GHCMod.hs | 38 ++-- test/BrowseSpec.hs | 4 +- test/CabalHelperSpec.hs | 2 +- test/CradleSpec.hs | 7 +- test/InfoSpec.hs | 2 +- test/TestUtils.hs | 16 +- 25 files changed, 390 insertions(+), 281 deletions(-) diff --git a/Language/Haskell/GhcMod/Browse.hs b/Language/Haskell/GhcMod/Browse.hs index 6093c9e..fce0a51 100644 --- a/Language/Haskell/GhcMod/Browse.hs +++ b/Language/Haskell/GhcMod/Browse.hs @@ -80,7 +80,7 @@ processExports :: (G.GhcMonad m, MonadIO m, ExceptionMonad m) processExports opt minfo = do let removeOps - | operators opt = id + | optOperators opt = id | otherwise = filter (isNotOp . getOccString) mapM (showExport opt minfo) $ removeOps $ G.modInfoExports minfo @@ -90,17 +90,17 @@ 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 + mqualified = (G.moduleNameString (G.moduleName $ G.nameModule e) ++ ".") `justIf` optQualified opt mtype :: m (Maybe String) mtype - | detailed opt = do + | optDetailed opt = do tyInfo <- G.modInfoLookupName minfo e -- If nothing found, load dependent module and lookup global tyResult <- maybe (inOtherModule e) (return . Just) tyInfo dflag <- G.getSessionDynFlags return $ do typeName <- tyResult >>= showThing dflag - (" :: " ++ typeName) `justIf` detailed opt + (" :: " ++ typeName) `justIf` optDetailed opt | otherwise = return Nothing formatOp nm | null nm = error "formatOp" diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index b339a12..8d80172 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -53,7 +53,7 @@ import Paths_ghc_mod as GhcMod -- | Only package related GHC options, sufficient for things that don't need to -- access home modules -getGhcMergedPkgOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) +getGhcMergedPkgOptions :: (Applicative m, IOish m, Gm m) => m [GHCOption] getGhcMergedPkgOptions = chCached $ \distdir -> Cached { cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), @@ -65,7 +65,7 @@ getGhcMergedPkgOptions = chCached $ \distdir -> Cached { return ([setupConfigPath distdir], opts) } -getCabalPackageDbStack :: (IOish m, GmEnv m, GmState m, GmLog m) => m [GhcPkgDb] +getCabalPackageDbStack :: (IOish m, Gm m) => m [GhcPkgDb] getCabalPackageDbStack = chCached $ \distdir -> Cached { cacheLens = Just (lGmcPackageDbStack . lGmCaches), cacheFile = pkgDbStackCacheFile distdir, @@ -86,7 +86,7 @@ chPkgToGhcPkg (ChPkgSpecific f) = PackageDb f -- -- The Component\'s 'gmcHomeModuleGraph' will be empty and has to be resolved by -- 'resolveGmComponents'. -getComponents :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) +getComponents :: (Applicative m, IOish m, Gm m) => m [GmComponent 'GMCRaw ChEntrypoint] getComponents = chCached$ \distdir -> Cached { cacheLens = Just (lGmcComponents . lGmCaches), @@ -116,7 +116,7 @@ getComponents = chCached$ \distdir -> Cached { , a == a' ] -prepareCabalHelper :: (IOish m, GmEnv m, GmLog m) => m () +prepareCabalHelper :: (IOish m, GmEnv m, GmOut m, GmLog m) => m () prepareCabalHelper = do crdl <- cradle let projdir = cradleRootDir crdl @@ -147,19 +147,19 @@ getStackPackageDbStack = do localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] "" return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb] -patchStackPrograms :: IOish m => OutputOpts -> Cradle -> Programs -> m Programs -patchStackPrograms _oopts crdl progs +patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs +patchStackPrograms crdl progs | cradleProjectType crdl /= StackProject = return progs -patchStackPrograms oopts crdl progs = do +patchStackPrograms crdl progs = do let projdir = cradleRootDir crdl - Just ghc <- liftIO $ getStackGhcPath oopts projdir - Just ghcPkg <- liftIO $ getStackGhcPkgPath oopts projdir + Just ghc <- getStackGhcPath projdir + Just ghcPkg <- getStackGhcPkgPath projdir return $ progs { ghcProgram = ghc , ghcPkgProgram = ghcPkg } -withCabal :: (IOish m, GmEnv m, GmLog m) => m a -> m a +withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a withCabal action = do crdl <- cradle opts <- options @@ -177,7 +177,7 @@ withCabal action = do pkgDbStackOutOfSync <- case mCusPkgDbStack of Just cusPkgDbStack -> do - pkgDb <- runQuery'' readProc (helperProgs $ programs opts) projdir distdir $ + pkgDb <- runQuery'' readProc (helperProgs $ optPrograms opts) projdir distdir $ map chPkgToGhcPkg <$> packageDbStack return $ pkgDb /= cusPkgDbStack @@ -199,10 +199,10 @@ withCabal action = do || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ case projType of CabalProject -> - cabalReconfigure readProc (programs opts) crdl projdir distdir + cabalReconfigure readProc (optPrograms opts) crdl projdir distdir StackProject -> - stackReconfigure crdl (programs opts) + stackReconfigure crdl (optPrograms opts) _ -> error $ "withCabal: unsupported project type: " ++ show projType @@ -216,7 +216,7 @@ withCabal action = do [ "--with-ghc=" ++ T.ghcProgram progs ] -- Only pass ghc-pkg if it was actually set otherwise we -- might break cabal's guessing logic - ++ if T.ghcPkgProgram progs /= T.ghcPkgProgram (programs defaultOptions) + ++ if T.ghcPkgProgram progs /= T.ghcPkgProgram (optPrograms defaultOptions) then [ "--with-ghc-pkg=" ++ T.ghcPkgProgram progs ] else [] ++ map pkgDbArg cusPkgStack @@ -277,7 +277,7 @@ helperProgs progs = CH.Programs { ghcPkgProgram = T.ghcPkgProgram progs } -chCached :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m, Serialize a) +chCached :: (Applicative m, IOish m, Gm m, Serialize a) => (FilePath -> Cached m GhcModState ChCacheData a) -> m a chCached c = do root <- cradleRootDir <$> cradle @@ -289,10 +289,8 @@ chCached c = do -- changes the cache files will be gone anyways ;) cacheInputData root = do opts <- options - let oopts = outputOpts opts - progs = programs opts crdl <- cradle - progs' <- patchStackPrograms oopts crdl progs + progs' <- patchStackPrograms crdl (optPrograms opts) return $ ( helperProgs progs' , root , (gmVer, chVer) diff --git a/Language/Haskell/GhcMod/CaseSplit.hs b/Language/Haskell/GhcMod/CaseSplit.hs index 3283e30..7c98f6e 100644 --- a/Language/Haskell/GhcMod/CaseSplit.hs +++ b/Language/Haskell/GhcMod/CaseSplit.hs @@ -6,7 +6,6 @@ module Language.Haskell.GhcMod.CaseSplit ( import Data.List (find, intercalate) import Data.Maybe (isJust) -import Data.Functor import qualified Data.Text as T import qualified Data.Text.IO as T (readFile) import System.FilePath @@ -50,7 +49,7 @@ splits :: IOish m -> GhcModT m String splits file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ do - oopts <- outputOpts <$> options + oopts <- outputOpts crdl <- cradle style <- getStyle dflag <- G.getSessionDynFlags @@ -70,7 +69,7 @@ splits file lineNo colNo = handler (SomeException ex) = do gmLog GmException "splits" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< outputOpts <$> options + emptyResult =<< outputOpts ---------------------------------------------------------------- -- a. Code for getting the information of the variable diff --git a/Language/Haskell/GhcMod/Convert.hs b/Language/Haskell/GhcMod/Convert.hs index 480bd04..89d39d5 100644 --- a/Language/Haskell/GhcMod/Convert.hs +++ b/Language/Haskell/GhcMod/Convert.hs @@ -25,11 +25,11 @@ inter _ [] = id inter c bs = foldr1 (\x y -> x . (c:) . y) bs convert' :: (ToString a, IOish m, GmEnv m) => a -> m String -convert' x = flip convert x . outputOpts <$> options +convert' x = flip convert x . optOutput <$> options convert :: ToString a => OutputOpts -> a -> String -convert opt@OutputOpts { outputStyle = LispStyle } x = toLisp opt x "\n" -convert opt@OutputOpts { outputStyle = PlainStyle } x +convert opt@OutputOpts { ooptStyle = LispStyle } x = toLisp opt x "\n" +convert opt@OutputOpts { ooptStyle = PlainStyle } x | str == "\n" = "" | otherwise = str where @@ -43,7 +43,7 @@ lineSep :: OutputOpts -> String lineSep oopts = interpret lsep where interpret s = read $ "\"" ++ s ++ "\"" - LineSeparator lsep = lineSeparator oopts + LineSeparator lsep = ooptLineSeparator oopts -- | -- diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 7a64d26..aa2d082 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -29,12 +29,16 @@ import Prelude -- Find a cabal file by tracing ancestor directories. -- Find a sandbox according to a cabal sandbox config -- in a cabal directory. -findCradle :: OutputOpts -> IO Cradle -findCradle oopts = findCradle' oopts =<< getCurrentDirectory +findCradle :: (IOish m, GmOut m) => m Cradle +findCradle = findCradle' =<< liftIO getCurrentDirectory -findCradle' :: OutputOpts -> FilePath -> IO Cradle -findCradle' oopts dir = run $ do - (stackCradle oopts dir `mplus` cabalCradle dir `mplus` sandboxCradle dir `mplus` plainCradle dir) +findCradle' :: (IOish m, GmOut m) => FilePath -> m Cradle +findCradle' dir = run $ + msum [ stackCradle dir + , cabalCradle dir + , sandboxCradle dir + , plainCradle dir + ] where run a = fillTempDir =<< (fromJust <$> runMaybeT a) findSpecCradle :: FilePath -> IO Cradle @@ -53,14 +57,14 @@ findSpecCradle dir = do cleanupCradle :: Cradle -> IO () cleanupCradle crdl = removeDirectoryRecursive $ cradleTempDir crdl -fillTempDir :: MonadIO m => Cradle -> m Cradle +fillTempDir :: IOish m => Cradle -> m Cradle fillTempDir crdl = do tmpDir <- liftIO $ newTempDir (cradleRootDir crdl) return crdl { cradleTempDir = tmpDir } -cabalCradle :: FilePath -> MaybeT IO Cradle +cabalCradle :: IOish m => FilePath -> MaybeT m Cradle cabalCradle wdir = do - cabalFile <- MaybeT $ findCabalFile wdir + cabalFile <- MaybeT $ liftIO $ findCabalFile wdir let cabalDir = takeDirectory cabalFile @@ -73,19 +77,19 @@ cabalCradle wdir = do , cradleDistDir = "dist" } -stackCradle :: OutputOpts -> FilePath -> MaybeT IO Cradle -stackCradle oopts wdir = do - cabalFile <- MaybeT $ findCabalFile wdir +stackCradle :: (IOish m, GmOut m) => FilePath -> MaybeT m Cradle +stackCradle wdir = do + cabalFile <- MaybeT $ liftIO $ findCabalFile wdir let cabalDir = takeDirectory cabalFile - _stackConfigFile <- MaybeT $ findStackConfigFile cabalDir + _stackConfigFile <- MaybeT $ liftIO $ findStackConfigFile cabalDir -- If dist/setup-config already exists the user probably wants to use cabal -- rather than stack, or maybe that's just me ;) whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero - distDir <- MaybeT $ getStackDistDir oopts cabalDir + distDir <- MaybeT $ getStackDistDir cabalDir return Cradle { cradleProjectType = StackProject @@ -96,9 +100,9 @@ stackCradle oopts wdir = do , cradleDistDir = distDir } -sandboxCradle :: FilePath -> MaybeT IO Cradle +sandboxCradle :: IOish m => FilePath -> MaybeT m Cradle sandboxCradle wdir = do - sbDir <- MaybeT $ findCabalSandboxDir wdir + sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir return Cradle { cradleProjectType = SandboxProject , cradleCurrentDir = wdir @@ -108,7 +112,7 @@ sandboxCradle wdir = do , cradleDistDir = "dist" } -plainCradle :: FilePath -> MaybeT IO Cradle +plainCradle :: IOish m => FilePath -> MaybeT m Cradle plainCradle wdir = do return $ Cradle { cradleProjectType = PlainProject diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index a1acd85..41f22b7 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -39,7 +39,7 @@ debugInfo = do fsep $ map text pkgOpts) , "GHC System libraries: " ++ ghcLibDir , "GHC user options:\n" ++ render (nest 4 $ - fsep $ map text ghcUserOptions) + fsep $ map text optGhcUserOptions) ] ++ cabal cabalDebug :: IOish m => GhcModT m [String] diff --git a/Language/Haskell/GhcMod/FillSig.hs b/Language/Haskell/GhcMod/FillSig.hs index f3c03a5..6ab76c1 100644 --- a/Language/Haskell/GhcMod/FillSig.hs +++ b/Language/Haskell/GhcMod/FillSig.hs @@ -78,7 +78,7 @@ sig :: IOish m -> GhcModT m String sig file lineNo colNo = runGmlT' [Left file] deferErrors $ ghandle fallback $ do - oopts <- outputOpts <$> options + oopts <- outputOpts style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file @@ -97,7 +97,7 @@ sig file lineNo colNo = in (rTy, fourInts loc, [initial ++ body]) where fallback (SomeException _) = do - oopts <- outputOpts <$> options + oopts <- outputOpts -- Code cannot be parsed by ghc module -- Fallback: try to get information via haskell-src-exts whenFound oopts (getSignatureFromHE file lineNo colNo) $ \x -> case x of @@ -347,7 +347,7 @@ refine :: IOish m refine file lineNo colNo (Expression expr) = ghandle handler $ runGmlT' [Left file] deferErrors $ do - oopts <- outputOpts <$> options + oopts <- outputOpts style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file @@ -367,7 +367,7 @@ refine file lineNo colNo (Expression expr) = handler (SomeException ex) = do gmLog GmException "refining" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< outputOpts <$> options + emptyResult =<< outputOpts -- Look for the variable in the specified position findVar @@ -424,7 +424,7 @@ auto :: IOish m -> GhcModT m String auto file lineNo colNo = ghandle handler $ runGmlT' [Left file] deferErrors $ do - oopts <- outputOpts <$> options + oopts <- outputOpts style <- getStyle dflag <- G.getSessionDynFlags modSum <- fileModSummaryWithMapping file @@ -456,7 +456,7 @@ auto file lineNo colNo = handler (SomeException ex) = do gmLog GmException "auto-refining" $ text "" $$ nest 4 (showDoc ex) - emptyResult =<< outputOpts <$> options + emptyResult =<< outputOpts -- Functions we do not want in completions notWantedFuns :: [String] diff --git a/Language/Haskell/GhcMod/HomeModuleGraph.hs b/Language/Haskell/GhcMod/HomeModuleGraph.hs index c9d103d..f39fdbf 100644 --- a/Language/Haskell/GhcMod/HomeModuleGraph.hs +++ b/Language/Haskell/GhcMod/HomeModuleGraph.hs @@ -126,7 +126,7 @@ pruneUnreachable smp0 gmg@GmModuleGraph {..} = let collapseMaybeSet :: Maybe (Set a) -> Set a collapseMaybeSet = maybe Set.empty id -homeModuleGraph :: (IOish m, GmLog m, GmEnv m, GmState m) +homeModuleGraph :: (IOish m, Gm m) => HscEnv -> Set ModulePath -> m GmModuleGraph homeModuleGraph env smp = updateHomeModuleGraph env mempty smp smp @@ -161,7 +161,7 @@ canonicalizeModuleGraph GmModuleGraph {..} = liftIO $ do fmg (mp, smp) = liftM2 (,) (canonicalizeModulePath mp) (Set.fromList <$> mapM canonicalizeModulePath (Set.toList smp)) -updateHomeModuleGraph :: (IOish m, GmLog m, GmEnv m, GmState m) +updateHomeModuleGraph :: (IOish m, Gm m) => HscEnv -> GmModuleGraph -> Set ModulePath -- ^ Initial set of modules @@ -187,7 +187,7 @@ mkModuleMap :: Set ModulePath -> Map ModuleName ModulePath mkModuleMap smp = Map.fromList $ map (mpModule &&& id) $ Set.toList smp updateHomeModuleGraph' - :: forall m. (MonadState S m, IOish m, GmLog m, GmEnv m, GmState m) + :: forall m. (MonadState S m, IOish m, Gm m) => HscEnv -> Set ModulePath -- ^ Initial set of modules -> m () diff --git a/Language/Haskell/GhcMod/Info.hs b/Language/Haskell/GhcMod/Info.hs index b3f1c52..31a8eab 100644 --- a/Language/Haskell/GhcMod/Info.hs +++ b/Language/Haskell/GhcMod/Info.hs @@ -3,7 +3,6 @@ module Language.Haskell.GhcMod.Info ( , types ) where -import Control.Applicative import Data.Function (on) import Data.List (sortBy) import Data.Maybe (catMaybes) @@ -35,8 +34,8 @@ info :: IOish m info file expr = ghandle handler $ runGmlT' [Left file] deferErrors $ - withInteractiveContext $ - convert . outputOpts <$> options <*> body + withInteractiveContext $ do + convert' =<< body where handler (SomeException ex) = do gmLog GmException "info" $ text "" $$ nest 4 (showDoc ex) diff --git a/Language/Haskell/GhcMod/Lint.hs b/Language/Haskell/GhcMod/Lint.hs index 735411a..b30ede0 100644 --- a/Language/Haskell/GhcMod/Lint.hs +++ b/Language/Haskell/GhcMod/Lint.hs @@ -20,7 +20,7 @@ lint :: IOish m lint file = do opt <- options withMappedFile file $ \tempfile -> - liftIO (hlint $ tempfile : "--quiet" : hlintOpts opt) + liftIO (hlint $ tempfile : "--quiet" : optHlintOpts opt) >>= mapM (replaceFileName tempfile) >>= ghandle handler . pack where diff --git a/Language/Haskell/GhcMod/Logger.hs b/Language/Haskell/GhcMod/Logger.hs index ef5b556..10ebd5b 100644 --- a/Language/Haskell/GhcMod/Logger.hs +++ b/Language/Haskell/GhcMod/Logger.hs @@ -30,7 +30,6 @@ import Language.Haskell.GhcMod.DynFlags (withDynFlags) import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Utils (mkRevRedirMapFunc) -import Language.Haskell.GhcMod.Types import qualified Language.Haskell.GhcMod.Gap as Gap import Prelude @@ -76,13 +75,13 @@ appendLogRef rfm df (LogRef ref) _ sev src st msg = do -- | Logged messages are returned as 'String'. -- Right is success and Left is failure. -withLogger :: (GmGhc m, GmEnv m, GmState m) +withLogger :: (GmGhc m, GmEnv m, GmOut m, GmState m) => (DynFlags -> DynFlags) -> m a -> m (Either String (String, a)) withLogger f action = do env <- G.getSession - oopts <- outputOpts <$> options + oopts <- outputOpts let conv = convert oopts eres <- withLogger' env $ \setDf -> withDynFlags (f . setDf) action diff --git a/Language/Haskell/GhcMod/Logging.hs b/Language/Haskell/GhcMod/Logging.hs index a7a1bea..86df113 100644 --- a/Language/Haskell/GhcMod/Logging.hs +++ b/Language/Haskell/GhcMod/Logging.hs @@ -65,7 +65,7 @@ decreaseLogLevel l = pred l -- True -- >>> Just GmDebug <= Just GmException -- False -gmLog :: (MonadIO m, GmLog m, GmEnv m) => GmLogLevel -> String -> Doc -> m () +gmLog :: (MonadIO m, GmLog m, GmOut m) => GmLogLevel -> String -> Doc -> m () gmLog level loc' doc = do GhcModLog { gmLogLevel = Just level' } <- gmlHistory @@ -78,7 +78,7 @@ gmLog level loc' doc = do gmlJournal (GhcModLog Nothing (Last Nothing) [(level, loc', msgDoc)]) -gmVomit :: (MonadIO m, GmLog m, GmEnv m) => String -> Doc -> String -> m () +gmVomit :: (MonadIO m, GmLog m, GmOut m, GmEnv m) => String -> Doc -> String -> m () gmVomit filename doc content = do gmLog GmVomit "" $ doc <+>: text content diff --git a/Language/Haskell/GhcMod/Modules.hs b/Language/Haskell/GhcMod/Modules.hs index a5766c6..2b78ac4 100644 --- a/Language/Haskell/GhcMod/Modules.hs +++ b/Language/Haskell/GhcMod/Modules.hs @@ -14,13 +14,13 @@ import qualified GHC as G ---------------------------------------------------------------- -- | Listing installed modules. -modules :: (IOish m, GmEnv m, GmState m, GmLog m) => m String +modules :: (IOish m, Gm m) => m String modules = do - Options { detailed } <- options + Options { optDetailed } <- options df <- runGmPkgGhc G.getSessionDynFlags let mns = listVisibleModuleNames df pmnss = map (first moduleNameString) $ zip mns (modulePkg df `map` mns) - convert' $ nub [ if detailed then pkg ++ " " ++ mn else mn + convert' $ nub [ if optDetailed then pkg ++ " " ++ mn else mn | (mn, pkgs) <- pmnss, pkg <- pkgs ] where modulePkg df = lookupModulePackageInAllPackages df diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 172b296..0ecdbc7 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -16,7 +16,8 @@ {-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.Monad ( - runGhcModT + runGmOutT + , runGhcModT , runGhcModT' , runGhcModT'' , hoistGhcModT @@ -51,26 +52,24 @@ import Exception (ExceptionMonad(..)) import System.Directory import Prelude -withCradle :: IOish m => OutputOpts -> FilePath -> (Cradle -> m a) -> m a -withCradle oopts cradledir f = - gbracket (liftIO $ findCradle' oopts cradledir) (liftIO . cleanupCradle) f +withCradle :: (IOish m, GmOut m) => FilePath -> (Cradle -> m a) -> m a +withCradle cradledir f = + gbracket (findCradle' cradledir) (liftIO . cleanupCradle) f -withGhcModEnv :: IOish m => FilePath -> Options -> (GhcModEnv -> m a) -> m a +withGhcModEnv :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a withGhcModEnv dir opts f = - withCradle (outputOpts opts) dir (withGhcModEnv' opts f) + withCradle dir (withGhcModEnv' opts f) -withGhcModEnv' :: IOish m => Options -> (GhcModEnv -> m a) -> Cradle -> m a +withGhcModEnv' :: (IOish m, GmOut m) => Options -> (GhcModEnv -> m a) -> Cradle -> m a withGhcModEnv' opts f crdl = do olddir <- liftIO getCurrentDirectory - c <- liftIO newChan - let outp = case linePrefix $ outputOpts opts of - Just _ -> GmOutputChan c - Nothing -> GmOutputStdio - gbracket_ (setup c) (teardown olddir) (f $ GhcModEnv opts crdl outp) + gbracket_ setup (teardown olddir) (f $ GhcModEnv opts crdl) where - setup c = liftIO $ do - setCurrentDirectory $ cradleRootDir crdl - forkIO $ stdoutGateway c + setup = do + c <- gmoChan <$> gmoAsk + liftIO $ do + setCurrentDirectory $ cradleRootDir crdl + forkIO $ stdoutGateway c teardown olddir tid = liftIO $ do setCurrentDirectory olddir @@ -92,10 +91,12 @@ runGhcModT' :: IOish m -> Options -> GhcModT m a -> m (Either GhcModError a, GhcModLog) -runGhcModT' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> - withGhcModEnv dir' opt $ \env -> - first (fst <$>) <$> runGhcModT'' env defaultGhcModState - (gmSetLogLevel (logLevel $ outputOpts opt) >> action) +runGhcModT' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> do + gmo <- GhcModOut (optOutput opt) <$> liftIO newChan + runGmOutT gmo $ + withGhcModEnv dir' opt $ \env -> + first (fst <$>) <$> runGhcModT'' env defaultGhcModState + (gmSetLogLevel (ooptLogLevel $ optOutput opt) >> action) -- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT -- computation. Note that if the computation that returned @result@ modified the @@ -108,6 +109,7 @@ hoistGhcModT (r,l) = do Left e -> throwError e Right a -> 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'. @@ -117,6 +119,9 @@ runGhcModT'' :: IOish m => GhcModEnv -> GhcModState -> GhcModT m a - -> m (Either GhcModError (a, GhcModState), GhcModLog) + -> GmOutT m (Either GhcModError (a, GhcModState), GhcModLog) runGhcModT'' r s a = do - flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGhcModT a) s + flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGmT a) s + +runGmOutT :: IOish m => GhcModOut -> GmOutT m a -> m a +runGmOutT gmo ma = flip runReaderT gmo $ unGmOutT ma diff --git a/Language/Haskell/GhcMod/Monad/Types.hs b/Language/Haskell/GhcMod/Monad/Types.hs index de5c3d0..60e2935 100644 --- a/Language/Haskell/GhcMod/Monad/Types.hs +++ b/Language/Haskell/GhcMod/Monad/Types.hs @@ -22,7 +22,9 @@ module Language.Haskell.GhcMod.Monad.Types ( -- * Monad Types - GhcModT(..) + GhcModT + , GmOutT(..) + , GmT(..) , GmlT(..) , LightGhc(..) , GmGhc @@ -43,8 +45,10 @@ module Language.Haskell.GhcMod.Monad.Types ( , GmEnv(..) , GmState(..) , GmLog(..) + , GmOut(..) , cradle , options + , outputOpts , withOptions , getCompilerMode , setCompilerMode @@ -113,20 +117,28 @@ import Prelude import qualified MonadUtils as GHC (MonadIO(..)) --- | This is basically a newtype wrapper around 'StateT', 'ErrorT', 'JournalT' --- and 'ReaderT' with custom instances for 'GhcMonad' and it's constraints that --- means you can run (almost) all functions from the GHC API on top of 'GhcModT' --- transparently. --- --- The inner monad @m@ should have instances for 'MonadIO' and --- 'MonadBaseControl' 'IO', in the common case this is simply 'IO'. Most @mtl@ --- monads already have 'MonadBaseControl' 'IO' instances, see the --- @monad-control@ package. -newtype GhcModT m a = GhcModT { - unGhcModT :: StateT GhcModState - (ErrorT GhcModError - (JournalT GhcModLog - (ReaderT GhcModEnv m) ) ) a +type GhcModT m = GmT (GmOutT m) + +newtype GmOutT m a = GmOutT { + unGmOutT :: ReaderT GhcModOut m a + } deriving ( Functor + , Applicative + , Alternative + , Monad + , MonadPlus + , MonadTrans + , MTL.MonadIO +#if DIFFERENT_MONADIO + , GHC.MonadIO +#endif + , GmLog + ) + +newtype GmT m a = GmT { + unGmT :: StateT GhcModState + (ErrorT GhcModError + (JournalT GhcModLog + (ReaderT GhcModEnv m) ) ) a } deriving ( Functor , Applicative , Alternative @@ -145,7 +157,6 @@ newtype GmlT m a = GmlT { unGmlT :: GhcModT m a } , Alternative , Monad , MonadPlus - , MonadTrans , MTL.MonadIO #if DIFFERENT_MONADIO , GHC.MonadIO @@ -166,6 +177,9 @@ newtype LightGhc a = LightGhc { unLightGhc :: ReaderT (IORef HscEnv) IO a } #endif ) +-------------------------------------------------- +-- Miscellaneous instances + #if DIFFERENT_MONADIO instance MTL.MonadIO m => GHC.MonadIO (ReaderT x m) where liftIO = MTL.liftIO @@ -191,13 +205,26 @@ instance MonadIO m => MonadIO (JournalT x m) where liftIO = MTL.liftIO instance MonadIO m => MonadIO (MaybeT m) where liftIO = MTL.liftIO -instance MonadIOC m => MonadIO (GhcModT m) where +instance MonadIOC m => MonadIO (GmOutT m) where + liftIO = MTL.liftIO +instance MonadIOC m => MonadIO (GmT m) where liftIO = MTL.liftIO instance MonadIOC m => MonadIO (GmlT m) where liftIO = MTL.liftIO instance MonadIO LightGhc where liftIO = MTL.liftIO +instance MonadTrans GmT where + lift = GmT . lift . lift . lift . lift +instance MonadTrans GmlT where + lift = GmlT . lift . lift + +-------------------------------------------------- +-- Gm Classes + +type Gm m = (GmEnv m, GmState m, GmLog m, GmOut m) + +-- GmEnv ----------------------------------------- class Monad m => GmEnv m where gmeAsk :: m GhcModEnv gmeAsk = gmeReader id @@ -208,18 +235,32 @@ class Monad m => GmEnv m where gmeLocal :: (GhcModEnv -> GhcModEnv) -> m a -> m a {-# MINIMAL (gmeAsk | gmeReader), gmeLocal #-} -type Gm m = (GmEnv m, GmState m, GmLog m) +instance Monad m => GmEnv (GmT m) where + gmeAsk = GmT ask + gmeReader = GmT . reader + gmeLocal f a = GmT $ local f (unGmT a) -instance Monad m => GmEnv (GhcModT m) where - gmeAsk = GhcModT ask - gmeReader = GhcModT . reader - gmeLocal f a = GhcModT $ local f (unGhcModT a) +instance GmEnv m => GmEnv (GmOutT m) where + gmeAsk = lift gmeAsk + gmeReader = lift . gmeReader + gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma)) instance GmEnv m => GmEnv (StateT s m) where gmeAsk = lift gmeAsk gmeReader = lift . gmeReader - gmeLocal f (StateT a) = StateT $ \s -> gmeLocal f (a s) + gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma)) +instance GmEnv m => GmEnv (JournalT GhcModLog m) where + gmeAsk = lift gmeAsk + gmeReader = lift . gmeReader + gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma)) + +instance GmEnv m => GmEnv (ErrorT GhcModError m) where + gmeAsk = lift gmeAsk + gmeReader = lift . gmeReader + gmeLocal f ma = gmLiftWithInner (\run -> gmeLocal f (run ma)) + +-- GmState --------------------------------------- class Monad m => GmState m where gmsGet :: m GhcModState gmsGet = gmsState (\s -> (s, s)) @@ -245,16 +286,17 @@ instance Monad m => GmState (StateT GhcModState m) where gmsPut = put gmsState = state -instance Monad m => GmState (GhcModT m) where - gmsGet = GhcModT get - gmsPut = GhcModT . put - gmsState = GhcModT . state +instance Monad m => GmState (GmT m) where + gmsGet = GmT get + gmsPut = GmT . put + gmsState = GmT . state instance GmState m => GmState (MaybeT m) where gmsGet = MaybeT $ Just `liftM` gmsGet gmsPut = MaybeT . (Just `liftM`) . gmsPut gmsState = MaybeT . (Just `liftM`) . gmsState +-- GmLog ----------------------------------------- class Monad m => GmLog m where gmlJournal :: GhcModLog -> m () gmlHistory :: m GhcModLog @@ -265,10 +307,10 @@ instance Monad m => GmLog (JournalT GhcModLog m) where gmlHistory = history gmlClear = clear -instance Monad m => GmLog (GhcModT m) where - gmlJournal = GhcModT . lift . lift . journal - gmlHistory = GhcModT $ lift $ lift history - gmlClear = GhcModT $ lift $ lift clear +instance Monad m => GmLog (GmT m) where + gmlJournal = GmT . lift . lift . journal + gmlHistory = GmT $ lift $ lift history + gmlClear = GmT $ lift $ lift clear instance (Monad m, GmLog m) => GmLog (ReaderT r m) where gmlJournal = lift . gmlJournal @@ -280,19 +322,32 @@ instance (Monad m, GmLog m) => GmLog (StateT s m) where gmlHistory = lift gmlHistory gmlClear = lift gmlClear -instance Monad m => MonadJournal GhcModLog (GhcModT m) where - journal !w = GhcModT $ lift $ lift $ (journal w) - history = GhcModT $ lift $ lift $ history - clear = GhcModT $ lift $ lift $ clear +-- GmOut ----------------------------------------- +class Monad m => GmOut m where + gmoAsk :: m GhcModOut -instance MonadTrans GhcModT where - lift = GhcModT . lift . lift . lift . lift +instance Monad m => GmOut (GmOutT m) where + gmoAsk = GmOutT ask -instance forall r m. MonadReader r m => MonadReader r (GhcModT m) where +instance Monad m => GmOut (GmlT m) where + gmoAsk = GmlT $ lift $ GmOutT ask + +instance GmOut m => GmOut (GmT m) where + gmoAsk = lift gmoAsk + +instance GmOut m => GmOut (StateT s m) where + gmoAsk = lift gmoAsk + +instance Monad m => MonadJournal GhcModLog (GmT m) where + journal !w = GmT $ lift $ lift $ (journal w) + history = GmT $ lift $ lift $ history + clear = GmT $ lift $ lift $ clear + +instance forall r m. MonadReader r m => MonadReader r (GmT m) where local f ma = gmLiftWithInner (\run -> local f (run ma)) ask = gmLiftInner ask -instance (Monoid w, MonadWriter w m) => MonadWriter w (GhcModT m) where +instance (Monoid w, MonadWriter w m) => MonadWriter w (GmT m) where tell = gmLiftInner . tell listen ma = liftWith (\run -> listen (run ma)) >>= \(sta, w) -> @@ -300,63 +355,91 @@ instance (Monoid w, MonadWriter w m) => MonadWriter w (GhcModT m) where pass maww = maww >>= gmLiftInner . pass . return -instance MonadState s m => MonadState s (GhcModT m) where - get = GhcModT $ lift $ lift $ lift get - put = GhcModT . lift . lift . lift . put - state = GhcModT . lift . lift . lift . state +instance MonadState s m => MonadState s (GmT m) where + get = GmT $ lift $ lift $ lift get + put = GmT . lift . lift . lift . put + state = GmT . lift . lift . lift . state + +-------------------------------------------------- +-- monad-control instances + +-- GmOutT ---------------------------------------- +instance (MonadBaseControl IO m) => MonadBase IO (GmOutT m) where + liftBase = GmOutT . liftBase + +instance (MonadBaseControl IO m) => MonadBaseControl IO (GmOutT m) where + type StM (GmOutT m) a = StM (ReaderT GhcModEnv m) a + liftBaseWith = defaultLiftBaseWith + restoreM = defaultRestoreM + {-# INLINE liftBaseWith #-} + {-# INLINE restoreM #-} + +instance MonadTransControl GmOutT where + type StT GmOutT a = StT (ReaderT GhcModEnv) a + liftWith = defaultLiftWith GmOutT unGmOutT + restoreT = defaultRestoreT GmOutT + + +-- GmlT ------------------------------------------ instance (MonadBaseControl IO m) => MonadBase IO (GmlT m) where liftBase = GmlT . liftBase instance (MonadBaseControl IO m) => MonadBaseControl IO (GmlT m) where - type StM (GmlT m) a = StM (GhcModT m) a + type StM (GmlT m) a = StM (GmT m) a liftBaseWith = defaultLiftBaseWith restoreM = defaultRestoreM {-# INLINE liftBaseWith #-} {-# INLINE restoreM #-} instance MonadTransControl GmlT where - type StT GmlT a = StT GhcModT a - liftWith = defaultLiftWith GmlT unGmlT - restoreT = defaultRestoreT GmlT + type StT GmlT a = StT GmT a + liftWith f = GmlT $ + liftWith $ \runGm -> + liftWith $ \runEnv -> + f $ \ma -> runEnv $ runGm $ unGmlT ma + restoreT = GmlT . restoreT . restoreT -instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where - liftBase = GhcModT . liftBase -instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where - type StM (GhcModT m) a = +-- GmT ------------------------------------------ + +instance (MonadBaseControl IO m) => MonadBase IO (GmT m) where + liftBase = GmT . liftBase + +instance (MonadBaseControl IO m) => MonadBaseControl IO (GmT m) where + type StM (GmT m) a = StM (StateT GhcModState (ErrorT GhcModError (JournalT GhcModLog (ReaderT GhcModEnv m) ) ) ) a - - liftBaseWith f = GhcModT (liftBaseWith $ \runInBase -> - f $ runInBase . unGhcModT) - - restoreM = GhcModT . restoreM + liftBaseWith f = GmT (liftBaseWith $ \runInBase -> + f $ runInBase . unGmT) + restoreM = GmT . restoreM {-# INLINE liftBaseWith #-} {-# INLINE restoreM #-} -instance MonadTransControl GhcModT where - type StT GhcModT a = (Either GhcModError (a, GhcModState), GhcModLog) - - liftWith f = GhcModT $ +instance MonadTransControl GmT where + type StT GmT a = (Either GhcModError (a, GhcModState), GhcModLog) + liftWith f = GmT $ liftWith $ \runS -> liftWith $ \runE -> liftWith $ \runJ -> liftWith $ \runR -> - f $ \ma -> runR $ runJ $ runE $ runS $ unGhcModT ma - restoreT = GhcModT . restoreT . restoreT . restoreT . restoreT + f $ \ma -> runR $ runJ $ runE $ runS $ unGmT ma + restoreT = GmT . restoreT . restoreT . restoreT . restoreT {-# INLINE liftWith #-} {-# INLINE restoreT #-} -gmLiftInner :: Monad m => m a -> GhcModT m a -gmLiftInner = GhcModT . lift . lift . lift . lift +gmLiftInner :: Monad m => m a -> GmT m a +gmLiftInner = GmT . lift . lift . lift . lift gmLiftWithInner :: (MonadTransControl t, Monad m, Monad (t m)) => (Run t -> m (StT t a)) -> t m a gmLiftWithInner f = liftWith f >>= restoreT . return +-------------------------------------------------- +-- GHC API instances ----------------------------- + -- 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 @@ -369,8 +452,6 @@ instance (MonadIO m, MonadBaseControl IO m) => GhcMonad (GmlT m) where getSession = gmlGetSession setSession = gmlSetSession --- --------------------------------------------------------------------- - gmlGetSession :: (MonadIO m, MonadBaseControl IO m) => GmlT m HscEnv gmlGetSession = do ref <- gmgsSession . fromJust . gmGhcSession <$> gmsGet @@ -381,7 +462,6 @@ gmlSetSession a = do ref <- gmgsSession . fromJust . gmGhcSession <$> gmsGet GHC.liftIO $ flip writeIORef a ref --- --------------------------------------------------------------------- instance GhcMonad LightGhc where getSession = (GHC.liftIO . readIORef) =<< LightGhc ask setSession a = (GHC.liftIO . flip writeIORef a) =<< LightGhc ask @@ -394,7 +474,14 @@ instance HasDynFlags LightGhc where getDynFlags = hsc_dflags <$> getSession #endif -instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GhcModT m) where +instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmOutT m) where + gcatch act handler = control $ \run -> + run act `gcatch` (run . handler) + + gmask = liftBaseOp gmask . liftRestore + where liftRestore f r = f $ liftBaseOp_ r + +instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (GmT m) where gcatch act handler = control $ \run -> run act `gcatch` (run . handler) @@ -437,6 +524,9 @@ instance (MonadIO m, MonadBaseControl IO m) => ExceptionMonad (ReaderT s m) wher options :: GmEnv m => m Options options = gmOptions `liftM` gmeAsk +outputOpts :: GmOut m => m OutputOpts +outputOpts = gmoOptions `liftM` gmoAsk + cradle :: GmEnv m => m Cradle cradle = gmCradle `liftM` gmeAsk diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 92d7f3e..2c75158 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -25,7 +25,6 @@ module Language.Haskell.GhcMod.Output ( , gmReadProcess , gmUnsafePutStr , gmUnsafeErrStr - , gmUnsafeReadProcess , stdoutGateway ) where @@ -64,38 +63,46 @@ toGmLines "" = GmLines GmPartial "" toGmLines s | isNewline (last s) = GmLines GmTerminated s toGmLines s = GmLines GmPartial s -outputFns :: (GmEnv m, MonadIO m') +outputFns :: (GmOut m, MonadIO m') => m (GmLines String -> m' (), GmLines String -> m' ()) -outputFns = do - oopts <- outputOpts `liftM` options - env <- gmeAsk - return $ outputFns' oopts (gmOutput env) +outputFns = + outputFns' <$> gmoAsk -outputFns' :: MonadIO m' - => OutputOpts - -> GmOutput - -> (GmLines String -> m' (), GmLines String -> m' ()) -outputFns' opts output = let - OutputOpts {..} = opts +pfxFns :: Maybe (String, String) -> (GmLines String -> GmLines String, GmLines String -> GmLines String) +pfxFns lpfx = case lpfx of + Nothing -> ( id, id ) + Just (op, ep) -> ( fmap $ pfx (op++), fmap $ pfx (ep++) ) + where + pfx f = withLines f - pfx f = withLines f +stdioOutputFns :: MonadIO m => Maybe (String, String) -> (GmLines String -> m (), GmLines String -> m ()) +stdioOutputFns lpfx = let + (outPfx, errPfx) = pfxFns lpfx + in + ( liftIO . putStr . unGmLine . outPfx + , liftIO . hPutStr stderr . unGmLine . errPfx) - outPfx, errPfx :: GmLines String -> GmLines String - (outPfx, errPfx) = - case linePrefix of - Nothing -> ( id, id ) - Just (op, ep) -> ( fmap $ pfx (op++), fmap $ pfx (ep++) ) +chanOutputFns :: MonadIO m + => Chan (GmStream, GmLines String) + -> Maybe (String, String) + -> (GmLines String -> m (), GmLines String -> m ()) +chanOutputFns c lpfx = let + (outPfx, errPfx) = pfxFns lpfx + in + ( liftIO . writeChan c . (,) GmOutStream . outPfx + , liftIO . writeChan c . (,) GmErrStream . errPfx) + +outputFns' :: + MonadIO m => GhcModOut -> (GmLines String -> m (), GmLines String -> m ()) +outputFns' (GhcModOut oopts c) = let + OutputOpts {..} = oopts in - case output of - GmOutputStdio -> - ( liftIO . putStr . unGmLine . outPfx - , liftIO . hPutStr stderr . unGmLine . errPfx) - GmOutputChan c -> - ( liftIO . writeChan c . (,) GmOut . outPfx - , liftIO . writeChan c . (,) GmErr .errPfx) + case ooptLinePrefix of + Nothing -> stdioOutputFns ooptLinePrefix + Just _ -> chanOutputFns c ooptLinePrefix gmPutStr, gmPutStrLn, gmErrStr, gmErrStrLn - :: (MonadIO m, GmEnv m) => String -> m () + :: (MonadIO m, GmOut m) => String -> m () gmPutStr str = do putOut <- fst `liftM` outputFns @@ -111,21 +118,16 @@ gmErrStr str = do -- | Only use these when you're sure there are no other writers on stdout gmUnsafePutStr, gmUnsafeErrStr :: MonadIO m => OutputOpts -> String -> m () -gmUnsafePutStr oopts = (fst $ outputFns' oopts GmOutputStdio) . toGmLines -gmUnsafeErrStr oopts = (snd $ outputFns' oopts GmOutputStdio) . toGmLines +gmUnsafePutStr oopts = (fst $ stdioOutputFns (ooptLinePrefix oopts)) . toGmLines +gmUnsafeErrStr oopts = (snd $ stdioOutputFns (ooptLinePrefix oopts)) . toGmLines -gmUnsafeReadProcess :: OutputOpts -> FilePath -> [String] -> String -> IO String -gmUnsafeReadProcess oopts = - readProcessStderrChan' (snd $ outputFns' oopts GmOutputStdio) - - -gmReadProcess :: GmEnv m => m (FilePath -> [String] -> String -> IO String) +gmReadProcess :: GmOut m => m (FilePath -> [String] -> String -> IO String) gmReadProcess = do - GhcModEnv {..} <- gmeAsk - case gmOutput of - GmOutputChan _ -> + GhcModOut {..} <- gmoAsk + case ooptLinePrefix gmoOptions of + Just _ -> readProcessStderrChan - GmOutputStdio -> + Nothing -> return $ readProcess stdoutGateway :: Chan (GmStream, GmLines String) -> IO () @@ -136,8 +138,8 @@ stdoutGateway chan = go ("", "") case ty of GmTerminated -> case stream of - GmOut -> putStr (obuf++l) >> hFlush stdout >> go ("", ebuf) - GmErr -> putStr (ebuf++l) >> hFlush stdout >> go (obuf, "") + GmOutStream -> putStr (obuf++l) >> hFlush stdout >> go ("", ebuf) + GmErrStream -> putStr (ebuf++l) >> hFlush stdout >> go (obuf, "") GmPartial -> case reverse $ lines l of [] -> go buf [x] -> go (appendBuf stream buf x) @@ -146,12 +148,12 @@ stdoutGateway chan = go ("", "") hFlush stdout go (appendBuf stream buf x) - appendBuf GmOut (obuf, ebuf) s = (obuf++s, ebuf) - appendBuf GmErr (obuf, ebuf) s = (obuf, ebuf++s) + appendBuf GmOutStream (obuf, ebuf) s = (obuf++s, ebuf) + appendBuf GmErrStream (obuf, ebuf) s = (obuf, ebuf++s) readProcessStderrChan :: - GmEnv m => m (FilePath -> [String] -> String -> IO String) + GmOut m => m (FilePath -> [String] -> String -> IO String) readProcessStderrChan = do (_, e :: GmLines String -> IO ()) <- outputFns return $ readProcessStderrChan' e diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 808c932..ca80bbd 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -24,6 +24,7 @@ import Control.Applicative import Control.Exception as E import Control.Monad import Control.Monad.Trans.Maybe +import Control.Monad.Trans.Class import Data.List import Data.Char import Data.Maybe @@ -35,6 +36,7 @@ import System.Process import System.Info.Extra import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Caching import Language.Haskell.GhcMod.Output import qualified Language.Haskell.GhcMod.Utils as U @@ -77,22 +79,22 @@ findCabalFile dir = do findStackConfigFile :: FilePath -> IO (Maybe FilePath) findStackConfigFile dir = mightExist (dir "stack.yaml") -getStackDistDir :: OutputOpts -> FilePath -> IO (Maybe FilePath) -getStackDistDir oopts projdir = U.withDirectory_ projdir $ runMaybeT $ do - takeWhile (/='\n') <$> readStack oopts ["path", "--dist-dir"] +getStackDistDir :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) +getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do + takeWhile (/='\n') <$> readStack ["path", "--dist-dir"] -getStackGhcPath :: OutputOpts -> FilePath -> IO (Maybe FilePath) -getStackGhcPath oopts = findExecutablesInStackBinPath oopts "ghc" +getStackGhcPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) +getStackGhcPath = findExecutablesInStackBinPath "ghc" -getStackGhcPkgPath :: OutputOpts -> FilePath -> IO (Maybe FilePath) -getStackGhcPkgPath oopts = findExecutablesInStackBinPath oopts "ghc-pkg" +getStackGhcPkgPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) +getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" -findExecutablesInStackBinPath :: OutputOpts -> String -> FilePath -> IO (Maybe FilePath) -findExecutablesInStackBinPath oopts exe projdir = +findExecutablesInStackBinPath :: (IOish m, GmOut m) => String -> FilePath -> m (Maybe FilePath) +findExecutablesInStackBinPath exe projdir = U.withDirectory_ projdir $ runMaybeT $ do path <- splitSearchPath . takeWhile (/='\n') - <$> readStack oopts ["path", "--bin-path"] - MaybeT $ listToMaybe <$> findExecutablesInDirectories' path exe + <$> readStack ["path", "--bin-path"] + MaybeT $ liftIO $ listToMaybe <$> findExecutablesInDirectories' path exe findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] findExecutablesInDirectories' path binary = @@ -103,11 +105,12 @@ findExecutablesInDirectories' path binary = exeExtension = if isWindows then "exe" else "" -readStack :: OutputOpts -> [String] -> MaybeT IO String -readStack oopts args = do - stack <- MaybeT $ findExecutable "stack" +readStack :: (IOish m, GmOut m) => [String] -> MaybeT m String +readStack args = do + stack <- MaybeT $ liftIO $ findExecutable "stack" + readProc <- lift gmReadProcess liftIO $ flip E.catch (\(e :: IOError) -> throw $ GMEStackBootrap $ show e) $ do - evaluate =<< gmUnsafeReadProcess oopts stack args "" + evaluate =<< readProc stack args "" -- | Get path to sandbox config file getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 2a23fc9..5cc6405 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -57,7 +57,7 @@ import Prelude hiding ((.)) import System.Directory import System.FilePath -runGmPkgGhc :: (IOish m, GmEnv m, GmState m, GmLog m) => LightGhc a -> m a +runGmPkgGhc :: (IOish m, Gm m) => LightGhc a -> m a runGmPkgGhc action = do pkgOpts <- packageGhcOptions withLightHscEnv pkgOpts $ \env -> liftIO $ runLightGhc env action @@ -116,14 +116,14 @@ runGmlTWith :: IOish m -> GhcModT m b runGmlTWith efnmns' mdf wrapper action = do crdl <- cradle - Options { ghcUserOptions } <- options + Options { optGhcUserOptions } <- options let (fns, mns) = partitionEithers efnmns' ccfns = map (cradleCurrentDir crdl ) fns cfns <- mapM getCanonicalFileNameSafe ccfns let serfnmn = Set.fromList $ map Right mns ++ map Left cfns opts <- targetGhcOptions crdl serfnmn - let opts' = opts ++ ["-O0"] ++ ghcUserOptions + let opts' = opts ++ ["-O0"] ++ optGhcUserOptions gmVomit "session-ghc-options" @@ -260,7 +260,7 @@ findCandidates scns = foldl1 Set.intersection scns pickComponent :: Set ChComponentName -> ChComponentName pickComponent scn = Set.findMin scn -packageGhcOptions :: (Applicative m, IOish m, GmEnv m, GmState m, GmLog m) +packageGhcOptions :: (Applicative m, IOish m, Gm m) => m [GHCOption] packageGhcOptions = do crdl <- cradle @@ -282,7 +282,7 @@ sandboxOpts crdl = do getSandboxPackageDbStack = ([GlobalDb] ++) . maybe [UserDb] return <$> getSandboxDb crdl -resolveGmComponent :: (IOish m, GmLog m, GmEnv m, GmState m) +resolveGmComponent :: (IOish m, Gm m) => Maybe [CompilationUnit] -- ^ Updated modules -> GmComponent 'GMCRaw (Set ModulePath) -> m (GmComponent 'GMCResolved (Set ModulePath)) @@ -308,7 +308,7 @@ resolveGmComponent mums c@GmComponent {..} = do [ "-optP-include", "-optP" ++ distDir macrosHeaderPath ] ] -resolveEntrypoint :: (IOish m, GmEnv m, GmLog m, GmState m) +resolveEntrypoint :: (IOish m, Gm m) => Cradle -> GmComponent 'GMCRaw ChEntrypoint -> m (GmComponent 'GMCRaw (Set ModulePath)) @@ -341,7 +341,7 @@ chModToMod :: ChModuleName -> ModuleName chModToMod (ChModuleName mn) = mkModuleName mn -resolveModule :: (IOish m, GmEnv m, GmLog m, GmState m) => +resolveModule :: (IOish m, Gm m) => HscEnv -> [FilePath] -> CompilationUnit -> m (Maybe ModulePath) resolveModule env _srcDirs (Right mn) = liftIO $ traverse canonicalizeModulePath =<< findModulePath env mn @@ -373,7 +373,7 @@ resolveModule env srcDirs (Left fn') = do type CompilationUnit = Either FilePath ModuleName -resolveGmComponents :: (IOish m, GmState m, GmLog m, GmEnv m) +resolveGmComponents :: (IOish m, Gm m) => Maybe [CompilationUnit] -- ^ Updated modules -> [GmComponent 'GMCRaw (Set ModulePath)] diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 8e95ceb..3043011 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -90,51 +90,51 @@ data Programs = Programs { data OutputOpts = OutputOpts { -- | Verbosity - logLevel :: GmLogLevel - , outputStyle :: OutputStyle + ooptLogLevel :: GmLogLevel + , ooptStyle :: OutputStyle -- | Line separator string. - , lineSeparator :: LineSeparator + , ooptLineSeparator :: LineSeparator -- | Stdout/err line multiplexing using prefix encoding. @fst@ is stdout, -- @snd@ is stderr prefix. - , linePrefix :: Maybe (String, String) + , ooptLinePrefix :: Maybe (String, String) } deriving (Show) data Options = Options { - outputOpts :: OutputOpts - , programs :: Programs + optOutput :: OutputOpts + , optPrograms :: Programs -- | GHC command line options set on the @ghc-mod@ command line - , ghcUserOptions:: [GHCOption] + , optGhcUserOptions :: [GHCOption] -- | If 'True', 'browse' also returns operators. - , operators :: Bool + , optOperators :: Bool -- | If 'True', 'browse' also returns types. - , detailed :: Bool + , optDetailed :: Bool -- | If 'True', 'browse' will return fully qualified name - , qualified :: Bool - , hlintOpts :: [String] - , fileMappings :: [(FilePath, Maybe FilePath)] + , optQualified :: Bool + , optHlintOpts :: [String] + , optFileMappings :: [(FilePath, Maybe FilePath)] } deriving (Show) -- | A default 'Options'. defaultOptions :: Options defaultOptions = Options { - outputOpts = OutputOpts { - outputStyle = PlainStyle - , lineSeparator = LineSeparator "\0" - , linePrefix = Nothing - , logLevel = GmWarning + optOutput = OutputOpts { + ooptLogLevel = GmWarning + , ooptStyle = PlainStyle + , ooptLineSeparator = LineSeparator "\0" + , ooptLinePrefix = Nothing } - , programs = Programs { + , optPrograms = Programs { ghcProgram = "ghc" , ghcPkgProgram = "ghc-pkg" , cabalProgram = "cabal" , stackProgram = "stack" } - , ghcUserOptions = [] - , operators = False - , detailed = False - , qualified = False - , hlintOpts = [] - , fileMappings = [] + , optGhcUserOptions = [] + , optOperators = False + , optDetailed = False + , optQualified = False + , optHlintOpts = [] + , optFileMappings = [] } ---------------------------------------------------------------- @@ -158,7 +158,7 @@ data Cradle = Cradle { } deriving (Eq, Show) -data GmStream = GmOut | GmErr +data GmStream = GmOutStream | GmErrStream deriving (Show) data GmLineType = GmTerminated | GmPartial @@ -170,13 +170,14 @@ data GmLines a = GmLines GmLineType a unGmLine :: GmLines a -> a unGmLine (GmLines _ s) = s -data GmOutput = GmOutputStdio - | GmOutputChan (Chan (GmStream, GmLines String)) - data GhcModEnv = GhcModEnv { gmOptions :: Options , gmCradle :: Cradle - , gmOutput :: GmOutput + } + +data GhcModOut = GhcModOut { + gmoOptions :: OutputOpts + , gmoChan :: Chan (GmStream, GmLines String) } data GhcModLog = GhcModLog { diff --git a/src/GHCMod.hs b/src/GHCMod.hs index a550f32..05961d6 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -251,27 +251,27 @@ globalArgSpec = [ option "v" ["verbose"] "Increase or set log level. (0-7)" $ optArg "LEVEL" $ \ml o -> Right $ case ml of Nothing -> - modify (lLogLevel . lOutputOpts) increaseLogLevel o + modify (lOoptLogLevel . lOptOutput) increaseLogLevel o Just l -> - set (lLogLevel . lOutputOpts) (toEnum $ min 7 $ read l) o + set (lOoptLogLevel . lOptOutput) (toEnum $ min 7 $ read l) o , option "s" [] "Be silent, set log level to 0" $ - NoArg $ \o -> Right $ set (lLogLevel . lOutputOpts) (toEnum 0) o + NoArg $ \o -> Right $ set (lOoptLogLevel . lOptOutput) (toEnum 0) o , option "l" ["tolisp"] "Format output as an S-Expression" $ - NoArg $ \o -> Right $ set (lOutputStyle . lOutputOpts) LispStyle o + NoArg $ \o -> Right $ set (lOoptStyle . lOptOutput) LispStyle o , option "b" ["boundary", "line-seperator"] "Output line separator"$ - reqArg "SEP" $ \s o -> Right $ set (lLineSeparator . lOutputOpts) (LineSeparator s) o + reqArg "SEP" $ \s o -> Right $ set (lOoptLineSeparator . lOptOutput) (LineSeparator s) o , option "" ["line-prefix"] "Output line separator"$ reqArg "OUT,ERR" $ \s o -> let [out, err] = splitOn "," s - in Right $ set (lLinePrefix . lOutputOpts) (Just (out, err)) o + in Right $ set (lOoptLinePrefix . lOptOutput) (Just (out, err)) o , option "g" ["ghcOpt", "ghc-option"] "Option to be passed to GHC" $ reqArg "OPT" $ \g o -> Right $ - o { ghcUserOptions = g : ghcUserOptions o } + o { optGhcUserOptions = g : optGhcUserOptions o } {- File map docs: @@ -313,19 +313,19 @@ Exposed functions: (s,"") -> (s, Nothing) (f,t) -> (f, Just t) in - Right $ o { fileMappings = m : fileMappings o } + Right $ o { optFileMappings = m : optFileMappings o } , option "" ["with-ghc"] "GHC executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lGhcProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lGhcProgram . lOptPrograms) p o , option "" ["with-ghc-pkg"] "ghc-pkg executable to use (only needed when guessing from GHC path fails)" $ - reqArg "PATH" $ \p o -> Right $ set (lGhcPkgProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lGhcPkgProgram . lOptPrograms) p o , option "" ["with-cabal"] "cabal-install executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lCabalProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lCabalProgram . lOptPrograms) p o , option "" ["with-stack"] "stack executable to use" $ - reqArg "PATH" $ \p o -> Right $ set (lStackProgram . lPrograms) p o + reqArg "PATH" $ \p o -> Right $ set (lStackProgram . lOptPrograms) p o , option "" ["version"] "print version information" $ NoArg $ \_ -> Left ["version"] @@ -406,7 +406,7 @@ progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ case globalCommands cmdArgs of Just s -> gmPutStr s Nothing -> do - forM_ (reverse $ fileMappings globalOptions) $ uncurry loadMMappedFiles + forM_ (reverse $ optFileMappings globalOptions) $ uncurry loadMMappedFiles ghcCommands cmdArgs where hndle action = do @@ -559,7 +559,7 @@ exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure exitError' :: Options -> String -> IO a exitError' opts msg = do - gmUnsafeErrStr (outputOpts opts) msg + gmUnsafeErrStr (optOutput opts) msg liftIO exitFailure fatalError :: String -> a @@ -654,24 +654,24 @@ locAction' cmd _ _ = throw $ InvalidCommandLine (Left cmd) modulesArgSpec :: [OptDescr (Options -> Either [String] Options)] modulesArgSpec = [ option "d" ["detailed"] "Print package modules belong to." $ - NoArg $ \o -> Right $ o { detailed = True } + NoArg $ \o -> Right $ o { optDetailed = True } ] hlintArgSpec :: [OptDescr (Options -> Either [String] Options)] hlintArgSpec = [ option "h" ["hlintOpt"] "Option to be passed to hlint" $ - reqArg "hlintOpt" $ \h o -> Right $ o { hlintOpts = h : hlintOpts o } + reqArg "hlintOpt" $ \h o -> Right $ o { optHlintOpts = h : optHlintOpts o } ] browseArgSpec :: [OptDescr (Options -> Either [String] Options)] browseArgSpec = [ option "o" ["operators"] "Also print operators." $ - NoArg $ \o -> Right $ o { operators = True } + NoArg $ \o -> Right $ o { optOperators = True } , option "d" ["detailed"] "Print symbols with accompanying signature." $ - NoArg $ \o -> Right $ o { detailed = True } + NoArg $ \o -> Right $ o { optDetailed = True } , option "q" ["qualified"] "Qualify symbols" $ - NoArg $ \o -> Right $ o { qualified = True } + NoArg $ \o -> Right $ o { optQualified = True } ] nukeCaches :: IOish m => GhcModT m () diff --git a/test/BrowseSpec.hs b/test/BrowseSpec.hs index 657f423..09d7e6c 100644 --- a/test/BrowseSpec.hs +++ b/test/BrowseSpec.hs @@ -17,12 +17,12 @@ spec = do describe "browse -d Data.Either" $ do it "contains functions (e.g. `either') including their type signature" $ do - syms <- run defaultOptions { detailed = True } + syms <- run defaultOptions { optDetailed = True } $ lines <$> browse "Data.Either" syms `shouldContain` ["either :: (a -> c) -> (b -> c) -> Either a b -> c"] it "contains type constructors (e.g. `Left') including their type signature" $ do - syms <- run defaultOptions { detailed = True} + syms <- run defaultOptions { optDetailed = True} $ lines <$> browse "Data.Either" syms `shouldContain` ["Left :: a -> Either a b"] diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 3c2aa4e..c69bbab 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -61,7 +61,7 @@ spec = do let tdir = "test/data/stack-project" [ghcOpts] <- map gmcGhcOpts . filter ((==ChExeName "new-template-exe") . gmcName) <$> runD' tdir getComponents let pkgs = pkgOptions ghcOpts - pkgs `shouldBe` ["base", "bytestring"] + sort pkgs `shouldBe` ["base", "bytestring"] it "extracts build dependencies" $ do let tdir = "test/data/cabal-project" diff --git a/test/CradleSpec.hs b/test/CradleSpec.hs index 642e51d..f38ee35 100644 --- a/test/CradleSpec.hs +++ b/test/CradleSpec.hs @@ -7,6 +7,7 @@ import Language.Haskell.GhcMod.Types import System.Directory (canonicalizePath) import System.FilePath (pathSeparator) import Test.Hspec +import TestUtils import Prelude import Dir @@ -36,14 +37,14 @@ spec = do it "returns the current directory" $ do withDirectory_ "/" $ do curDir <- stripLastDot <$> canonicalizePath "/" - res <- clean_ $ findCradle (outputOpts defaultOptions) + res <- clean_ $ runGmOutDef findCradle cradleCurrentDir res `shouldBe` curDir cradleRootDir res `shouldBe` curDir cradleCabalFile res `shouldBe` Nothing it "finds a cabal file and a sandbox" $ do withDirectory "test/data/cabal-project/subdir1/subdir2" $ \dir -> do - res <- relativeCradle dir <$> clean_ (findCradle (outputOpts defaultOptions)) + res <- relativeCradle dir <$> clean_ (runGmOutDef findCradle) cradleCurrentDir res `shouldBe` "test/data/cabal-project/subdir1/subdir2" @@ -55,7 +56,7 @@ spec = do it "works even if a sandbox config file is broken" $ do withDirectory "test/data/broken-sandbox" $ \dir -> do - res <- relativeCradle dir <$> clean_ (findCradle (outputOpts defaultOptions)) + res <- relativeCradle dir <$> clean_ (runGmOutDef findCradle) cradleCurrentDir res `shouldBe` "test" "data" "broken-sandbox" diff --git a/test/InfoSpec.hs b/test/InfoSpec.hs index bd689a7..3bdd5ae 100644 --- a/test/InfoSpec.hs +++ b/test/InfoSpec.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} module InfoSpec where -import Control.Applicative ((<$>)) +import Control.Applicative import Data.List (isPrefixOf) import Language.Haskell.GhcMod #if __GLASGOW_HASKELL__ < 706 diff --git a/test/TestUtils.hs b/test/TestUtils.hs index 5f69d05..d5a1429 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -5,6 +5,7 @@ module TestUtils ( , runD' , runE , runNullLog + , runGmOutDef , shouldReturnError , isPkgDbAt , isPkgConfDAt @@ -19,6 +20,7 @@ import Language.Haskell.GhcMod.Types import Control.Arrow import Control.Category +import Control.Concurrent import Control.Applicative import Control.Monad.Error (ErrorT, runErrorT) import Control.Monad.Trans.Journal @@ -46,7 +48,7 @@ withSpecCradle :: IOish m => FilePath -> (Cradle -> m a) -> m a withSpecCradle cradledir f = gbracket (liftIO $ findSpecCradle cradledir) (liftIO . cleanupCradle) f -withGhcModEnvSpec :: IOish m => FilePath -> Options -> (GhcModEnv -> m a) -> m a +withGhcModEnvSpec :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a withGhcModEnvSpec dir opt f = withSpecCradle dir $ withGhcModEnv' opt f runGhcModTSpec :: Options -> GhcModT IO a -> IO (Either GhcModError a, GhcModLog) @@ -56,10 +58,12 @@ runGhcModTSpec opt action = do runGhcModTSpec' :: IOish m => FilePath -> Options -> GhcModT m b -> m (Either GhcModError b, GhcModLog) -runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> +runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> do + gmo <- GhcModOut (optOutput opt) <$> liftIO newChan + runGmOutT gmo $ withGhcModEnvSpec dir' opt $ \env -> do first (fst <$>) <$> runGhcModT'' env defaultGhcModState - (gmSetLogLevel (logLevel $ outputOpts opt) >> action) + (gmSetLogLevel (ooptLogLevel $ optOutput opt) >> action) -- | Run GhcMod run :: Options -> GhcModT IO a -> IO a @@ -75,7 +79,7 @@ runD' dir = extract . runGhcModTSpec' dir (setLogLevel testLogLevel defaultOptions) setLogLevel :: GmLogLevel -> Options -> Options -setLogLevel = set (lLogLevel . lOutputOpts) +setLogLevel = set (lOoptLogLevel . lOptOutput) runE :: ErrorT e IO a -> IO (Either e a) runE = runErrorT @@ -86,6 +90,10 @@ runNullLog action = do liftIO $ print w return a +runGmOutDef :: IOish m => GmOutT m a -> m a +runGmOutDef = + runGmOutT (GhcModOut (optOutput defaultOptions) (error "no chan")) + shouldReturnError :: Show a => IO (Either GhcModError a, GhcModLog) -> Expectation From a0a74332126999a3fd75c2f6a697bf33fff24124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 1 Sep 2015 10:45:15 +0200 Subject: [PATCH 096/147] Fix <7.10 --- Language/Haskell/GhcMod/Output.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 2c75158..7fcf0af 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -66,7 +66,7 @@ toGmLines s = GmLines GmPartial s outputFns :: (GmOut m, MonadIO m') => m (GmLines String -> m' (), GmLines String -> m' ()) outputFns = - outputFns' <$> gmoAsk + outputFns' `liftM` gmoAsk pfxFns :: Maybe (String, String) -> (GmLines String -> GmLines String, GmLines String -> GmLines String) pfxFns lpfx = case lpfx of From 82c5069cd84a0908df90ca88a90d0b59427abc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Sep 2015 04:00:30 +0200 Subject: [PATCH 097/147] Fix doctest --- Language/Haskell/GhcMod/Convert.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/Convert.hs b/Language/Haskell/GhcMod/Convert.hs index 89d39d5..fa266d2 100644 --- a/Language/Haskell/GhcMod/Convert.hs +++ b/Language/Haskell/GhcMod/Convert.hs @@ -47,9 +47,9 @@ lineSep oopts = interpret lsep -- | -- --- >>> toLisp (outputOpts defaultOptions) "fo\"o" "" +-- >>> toLisp (optOutput defaultOptions) "fo\"o" "" -- "\"fo\\\"o\"" --- >>> toPlain (outputOpts defaultOptions) "foo" "" +-- >>> toPlain (optOutput defaultOptions) "foo" "" -- "foo" instance ToString String where toLisp oopts = quote oopts @@ -57,9 +57,9 @@ instance ToString String where -- | -- --- >>> toLisp (outputOpts defaultOptions) ["foo", "bar", "ba\"z"] "" +-- >>> toLisp (optOutput defaultOptions) ["foo", "bar", "ba\"z"] "" -- "(\"foo\" \"bar\" \"ba\\\"z\")" --- >>> toPlain (outputOpts defaultOptions) ["foo", "bar", "baz"] "" +-- >>> toPlain (optOutput defaultOptions) ["foo", "bar", "baz"] "" -- "foo\nbar\nbaz" instance ToString [String] where toLisp oopts = toSexp1 oopts @@ -72,9 +72,9 @@ instance ToString [ModuleString] where -- | -- -- >>> let inp = [((1,2,3,4),"foo"),((5,6,7,8),"bar")] :: [((Int,Int,Int,Int),String)] --- >>> toLisp (outputOpts defaultOptions) inp "" +-- >>> toLisp (optOutput defaultOptions) inp "" -- "((1 2 3 4 \"foo\") (5 6 7 8 \"bar\"))" --- >>> toPlain (outputOpts defaultOptions) inp "" +-- >>> toPlain (optOutput defaultOptions) inp "" -- "1 2 3 4 \"foo\"\n5 6 7 8 \"bar\"" instance ToString [((Int,Int,Int,Int),String)] where toLisp oopts = toSexp2 . map toS From a8c111cda322d3ce730f2ce1920b52299c5115c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Sep 2015 04:57:25 +0200 Subject: [PATCH 098/147] Write cabal_macros.h on reconfigure when using stack --- Language/Haskell/GhcMod/CabalHelper.hs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 8d80172..3c04d68 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -209,6 +209,12 @@ withCabal action = do action where + writeAutogen projdir distdir = do + readProc <- gmReadProcess + gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" + liftIO $ writeAutogenFiles readProc projdir distdir + + cabalReconfigure readProc progs crdl projdir distdir = do withDirectory_ (cradleRootDir crdl) $ do cusPkgStack <- maybe [] ((PackageDb "clear"):) <$> getCustomPkgDbStack @@ -221,16 +227,19 @@ withCabal action = do else [] ++ map pkgDbArg cusPkgStack liftIO $ void $ readProc (T.cabalProgram progs) ("configure":progOpts) "" - gmLog GmDebug "" $ strDoc $ "writing Cabal autogen files" - liftIO $ writeAutogenFiles readProc projdir distdir + writeAutogen projdir distdir stackReconfigure crdl progs = do + let projdir = cradleRootDir crdl + distdir = projdir cradleDistDir crdl + withDirectory_ (cradleRootDir crdl) $ do supported <- haveStackSupport if supported then do spawn [T.stackProgram progs, "build", "--only-dependencies"] spawn [T.stackProgram progs, "build", "--only-configure"] + writeAutogen projdir distdir else gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 1.4.0.0)" From 996016ac7b7cc75c367f5560e0aae45655726e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Sep 2015 05:02:13 +0200 Subject: [PATCH 099/147] Fix wrong stack version lower bound in log message --- Language/Haskell/GhcMod/CabalHelper.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 3c04d68..f874ced 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -241,7 +241,7 @@ withCabal action = do spawn [T.stackProgram progs, "build", "--only-configure"] writeAutogen projdir distdir else - gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 1.4.0.0)" + gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 0.1.4.0)" spawn [] = return () spawn (exe:args) = do From 86c157d1d2ec1294cc14d3d7435019e91a03a70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Sep 2015 05:30:00 +0200 Subject: [PATCH 100/147] Add some more debug output --- Language/Haskell/GhcMod/Debug.hs | 13 ++++++++++++- Language/Haskell/GhcMod/Target.hs | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 41f22b7..2d363a8 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -15,6 +15,7 @@ import Language.Haskell.GhcMod.Internal import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.Pretty import Language.Haskell.GhcMod.Utils +import Language.Haskell.GhcMod.PathsAndFiles ---------------------------------------------------------------- @@ -27,7 +28,7 @@ debugInfo = do cabal <- case cradleProjectType of CabalProject -> cabalDebug - StackProject -> cabalDebug + StackProject -> (++) <$> stackPaths <*> cabalDebug _ -> return [] pkgOpts <- packageGhcOptions @@ -42,6 +43,16 @@ debugInfo = do fsep $ map text optGhcUserOptions) ] ++ cabal +stackPaths :: IOish m => GhcModT m [String] +stackPaths = do + Cradle {..} <- cradle + Just ghc <- getStackGhcPath cradleRootDir + Just ghcPkg <- getStackGhcPkgPath cradleRootDir + return $ + [ "Stack ghc executable: " ++ show ghc + , "Stack ghc-pkg executable:" ++ show ghcPkg + ] + cabalDebug :: IOish m => GhcModT m [String] cabalDebug = do Cradle {..} <- cradle diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 5cc6405..f1072ca 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -288,6 +288,7 @@ resolveGmComponent :: (IOish m, Gm m) -> m (GmComponent 'GMCResolved (Set ModulePath)) resolveGmComponent mums c@GmComponent {..} = do distDir <- cradleDistDir <$> cradle + gmLog GmDebug "resolveGmComponent" $ text $ show $ ghcOpts distDir withLightHscEnv (ghcOpts distDir) $ \env -> do let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs let mg = gmcHomeModuleGraph @@ -313,6 +314,7 @@ resolveEntrypoint :: (IOish m, Gm m) -> GmComponent 'GMCRaw ChEntrypoint -> m (GmComponent 'GMCRaw (Set ModulePath)) resolveEntrypoint Cradle {..} c@GmComponent {..} = do + gmLog GmDebug "resolveEntrypoint" $ text $ show $ gmcGhcSrcOpts withLightHscEnv gmcGhcSrcOpts $ \env -> do let srcDirs = if null gmcSourceDirs then [""] else gmcSourceDirs eps <- liftIO $ resolveChEntrypoints cradleRootDir gmcEntrypoints From 8fea4ac426331cc919a83a4c216c682e50454b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 2 Sep 2015 07:19:11 +0200 Subject: [PATCH 101/147] Fix `doc` command not using right ghc-pkg exe --- Language/Haskell/GhcMod/GhcPkg.hs | 13 +++++++++++++ Language/Haskell/GhcMod/PkgDoc.hs | 8 +++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index df2bce2..1d7d2d2 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -6,6 +6,7 @@ module Language.Haskell.GhcMod.GhcPkg ( , ghcDbOpt , getPackageDbStack , getPackageCachePaths + , getGhcPkgProgram ) where import Config (cProjectVersion, cTargetPlatformString, cProjectVersionInt) @@ -59,6 +60,18 @@ ghcDbOpt (PackageDb pkgDb) ---------------------------------------------------------------- +getGhcPkgProgram :: IOish m => GhcModT m FilePath +getGhcPkgProgram = do + crdl <- cradle + progs <- optPrograms <$> options + case cradleProjectType crdl of + StackProject -> do + Just ghcPkg <- getStackGhcPkgPath (cradleRootDir crdl) + return ghcPkg + _ -> + return $ ghcPkgProgram progs + + getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb] getPackageDbStack = do crdl <- cradle diff --git a/Language/Haskell/GhcMod/PkgDoc.hs b/Language/Haskell/GhcMod/PkgDoc.hs index b469f87..6ec5d0d 100644 --- a/Language/Haskell/GhcMod/PkgDoc.hs +++ b/Language/Haskell/GhcMod/PkgDoc.hs @@ -3,7 +3,7 @@ module Language.Haskell.GhcMod.PkgDoc (pkgDoc) where import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.Monad -import Language.Haskell.GhcMod.Utils +import Language.Haskell.GhcMod.Output import Control.Applicative import Prelude @@ -11,12 +11,14 @@ import Prelude -- | Obtaining the package name and the doc path of a module. pkgDoc :: IOish m => String -> GhcModT m String pkgDoc mdl = do + ghcPkg <- getGhcPkgProgram + readProc <- gmReadProcess pkgDbStack <- getPackageDbStack - pkg <- liftIO $ trim <$> readProcess "ghc-pkg" (toModuleOpts pkgDbStack) "" + pkg <- liftIO $ trim <$> readProc ghcPkg (toModuleOpts pkgDbStack) "" if pkg == "" then return "\n" else do - htmlpath <- liftIO $ readProcess "ghc-pkg" (toDocDirOpts pkg pkgDbStack) "" + htmlpath <- liftIO $ readProc ghcPkg (toDocDirOpts pkg pkgDbStack) "" let ret = pkg ++ " " ++ drop 14 htmlpath return ret where From be293db5035c3e4913e69dfaf64a448c8139f121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 7 Sep 2015 05:15:29 +0200 Subject: [PATCH 102/147] Update for cabal-helper >= 0.6 --- Language/Haskell/GhcMod/CabalHelper.hs | 53 +++++++++++++++++--------- ghc-mod.cabal | 2 +- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index f874ced..f525d40 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -58,10 +58,8 @@ getGhcMergedPkgOptions :: (Applicative m, IOish m, Gm m) getGhcMergedPkgOptions = chCached $ \distdir -> Cached { cacheLens = Just (lGmcMergedPkgOptions . lGmCaches), cacheFile = mergedPkgOptsCacheFile distdir, - cachedAction = \ _tcf (progs, rootdir, _) _ma -> do - readProc <- gmReadProcess - opts <- withCabal $ runQuery'' readProc progs rootdir distdir $ - ghcMergedPkgOptions + cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do + opts <- withCabal $ runCHQuery ghcMergedPkgOptions return ([setupConfigPath distdir], opts) } @@ -69,10 +67,10 @@ getCabalPackageDbStack :: (IOish m, Gm m) => m [GhcPkgDb] getCabalPackageDbStack = chCached $ \distdir -> Cached { cacheLens = Just (lGmcPackageDbStack . lGmCaches), cacheFile = pkgDbStackCacheFile distdir, - cachedAction = \ _tcf (progs, rootdir, _) _ma -> do + cachedAction = \_tcf (_progs, _projdir, _ver) _ma -> do crdl <- cradle - readProc <- gmReadProcess - dbs <- withCabal $ map chPkgToGhcPkg <$> runQuery'' readProc progs rootdir distdir packageDbStack + dbs <- withCabal $ map chPkgToGhcPkg <$> + runCHQuery packageDbStack return ([setupConfigFile crdl, sandboxConfigFile crdl], dbs) } @@ -91,9 +89,8 @@ getComponents :: (Applicative m, IOish m, Gm m) getComponents = chCached$ \distdir -> Cached { cacheLens = Just (lGmcComponents . lGmCaches), cacheFile = cabalHelperCacheFile distdir, - cachedAction = \ _tcf (progs, rootdir, _vers) _ma -> do - readProc <- gmReadProcess - runQuery'' readProc progs rootdir distdir $ do + cachedAction = \ _tcf (_progs, _projdir, _ver) _ma -> do + runCHQuery $ do q <- join7 <$> ghcOptions <*> ghcPkgOptions @@ -115,6 +112,23 @@ getComponents = chCached$ \distdir -> Cached { , (a', c) <- lc , a == a' ] +runCHQuery :: (IOish m, GmOut m, GmEnv m) => Query m b -> m b +runCHQuery a = do + crdl <- cradle + let projdir = cradleRootDir crdl + distdir = projdir cradleDistDir crdl + + opts <- options + progs <- patchStackPrograms crdl (optPrograms opts) + + readProc <- gmReadProcess + + let qe = (defaultQueryEnv projdir distdir) { + qeReadProcess = readProc + , qePrograms = helperProgs progs + } + runQuery qe a + prepareCabalHelper :: (IOish m, GmEnv m, GmOut m, GmLog m) => m () prepareCabalHelper = do @@ -177,8 +191,11 @@ withCabal action = do pkgDbStackOutOfSync <- case mCusPkgDbStack of Just cusPkgDbStack -> do - pkgDb <- runQuery'' readProc (helperProgs $ optPrograms opts) projdir distdir $ - map chPkgToGhcPkg <$> packageDbStack + let qe = (defaultQueryEnv projdir distdir) { + qeReadProcess = readProc + , qePrograms = helperProgs $ optPrograms opts + } + pkgDb <- runQuery qe $ map chPkgToGhcPkg <$> packageDbStack return $ pkgDb /= cusPkgDbStack Nothing -> return False @@ -289,19 +306,19 @@ helperProgs progs = CH.Programs { chCached :: (Applicative m, IOish m, Gm m, Serialize a) => (FilePath -> Cached m GhcModState ChCacheData a) -> m a chCached c = do - root <- cradleRootDir <$> cradle - dist <- cradleDistDir <$> cradle - d <- cacheInputData root - withCabal $ cached root (c dist) d + projdir <- cradleRootDir <$> cradle + distdir <- (projdir ) . cradleDistDir <$> cradle + d <- cacheInputData projdir + withCabal $ cached projdir (c distdir) d where -- we don't need to include the disdir in the cache input because when it -- changes the cache files will be gone anyways ;) - cacheInputData root = do + cacheInputData projdir = do opts <- options crdl <- cradle progs' <- patchStackPrograms crdl (optPrograms opts) return $ ( helperProgs progs' - , root + , projdir , (gmVer, chVer) ) diff --git a/ghc-mod.cabal b/ghc-mod.cabal index d24f528..a403a26 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -146,7 +146,7 @@ Library , bytestring , cereal >= 0.4 , containers - , cabal-helper == 0.5.* && >= 0.5.1.0 + , cabal-helper == 0.6.* && >= 0.6.0.0 , deepseq , directory , filepath From 358c64a14eb1a6ec22501f6e06c63c67099ce510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 7 Sep 2015 05:22:38 +0200 Subject: [PATCH 103/147] Install cabal-helper from git before --only-depends --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d836c9b..8873a1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,9 +40,9 @@ install: - happy --version # - ls -lR ~/.ghc # - ls -lR ~/.cabal - - cabal install -j --only-dependencies --enable-tests - git clone --depth=1 https://github.com/DanielG/cabal-helper.git - cabal install cabal-helper/ + - cabal install -j --only-dependencies --enable-tests script: From 2b7249e445a383fe3f11a87fde9ee05e1cf5b4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 7 Sep 2015 06:24:23 +0200 Subject: [PATCH 104/147] Fix travis --- .travis.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8873a1f..c110a3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,17 +31,13 @@ before_install: - stack --version install: + - export CABAL_VER="$(ghc-pkg describe ghc | sed -n '/^depends:/,/^[a-z]/p' | head -n-1 | sed '1{s/^depends://}' | grep " *Cabal" | tr -d "[:space:]" | sed 's/^Cabal-\([0-9.]*\)-.*/\1/g')" + - echo $CABAL_VER - cabal update -# - ( $CABAL122 && cabal install cabal-install --constraint "Cabal >= 1.22" && ghc-pkg unregister Cabal ) || true - - echo $PATH - - which cabal - - if [ -n "$(cabal --version | grep 'Cabal library' | awk '{ print $3 }' | tail -n1 | sed -n '/^1.18/p')" ]; then cabal install cabal-install --constraint "Cabal == 1.18.* && > 1.18.0"; fi - cabal install happy - happy --version -# - ls -lR ~/.ghc -# - ls -lR ~/.cabal - git clone --depth=1 https://github.com/DanielG/cabal-helper.git - - cabal install cabal-helper/ + - cabal install cabal-helper/ --constraint "Cabal == ${CABAL_VER}" - cabal install -j --only-dependencies --enable-tests From 5713fd9908742161b365b42ced8d1952b753fd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 7 Sep 2015 03:49:12 +0200 Subject: [PATCH 105/147] Fix custom package-db stack for non Cabal projects --- Language/Haskell/GhcMod/CabalHelper.hs | 14 +----------- Language/Haskell/GhcMod/CustomPackageDb.hs | 25 ++++++++++++++++++++++ Language/Haskell/GhcMod/GhcPkg.hs | 1 + Language/Haskell/GhcMod/PathsAndFiles.hs | 5 +++-- Language/Haskell/GhcMod/Target.hs | 6 ++++-- README.md | 2 +- ghc-mod.cabal | 1 + 7 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 Language/Haskell/GhcMod/CustomPackageDb.hs diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index f525d40..0fa2fee 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -21,7 +21,6 @@ module Language.Haskell.GhcMod.CabalHelper , getGhcMergedPkgOptions , getCabalPackageDbStack , getStackPackageDbStack - , getCustomPkgDbStack , prepareCabalHelper ) #endif @@ -43,6 +42,7 @@ import Language.Haskell.GhcMod.Utils import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Output +import Language.Haskell.GhcMod.CustomPackageDb import System.FilePath import System.Directory (findExecutable) import System.Process @@ -139,18 +139,6 @@ prepareCabalHelper = do when (cradleProjectType crdl == CabalProject || cradleProjectType crdl == StackProject) $ withCabal $ liftIO $ prepare readProc projdir distdir -parseCustomPackageDb :: String -> [GhcPkgDb] -parseCustomPackageDb src = map parsePkgDb $ filter (not . null) $ lines src - where - parsePkgDb "global" = GlobalDb - parsePkgDb "user" = UserDb - parsePkgDb s = PackageDb s - -getCustomPkgDbStack :: (IOish m, GmEnv m) => m (Maybe [GhcPkgDb]) -getCustomPkgDbStack = do - mCusPkgDbFile <- liftIO . (traverse readFile <=< findCustomPackageDbFile) . cradleRootDir =<< cradle - return $ parseCustomPackageDb <$> mCusPkgDbFile - getStackPackageDbStack :: IOish m => m [GhcPkgDb] getStackPackageDbStack = do mstack <- liftIO $ findExecutable "stack" diff --git a/Language/Haskell/GhcMod/CustomPackageDb.hs b/Language/Haskell/GhcMod/CustomPackageDb.hs new file mode 100644 index 0000000..ce548eb --- /dev/null +++ b/Language/Haskell/GhcMod/CustomPackageDb.hs @@ -0,0 +1,25 @@ +module Language.Haskell.GhcMod.CustomPackageDb where + + +import Control.Applicative +import Control.Monad +import Control.Category ((.)) +import Data.Maybe +import Data.Traversable +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types +import Language.Haskell.GhcMod.PathsAndFiles +import Prelude hiding ((.)) + + +parseCustomPackageDb :: String -> [GhcPkgDb] +parseCustomPackageDb src = map parsePkgDb $ filter (not . null) $ lines src + where + parsePkgDb "global" = GlobalDb + parsePkgDb "user" = UserDb + parsePkgDb s = PackageDb s + +getCustomPkgDbStack :: (IOish m, GmEnv m) => m (Maybe [GhcPkgDb]) +getCustomPkgDbStack = do + mCusPkgDbFile <- liftIO . (traverse readFile <=< findCustomPackageDbFile) . cradleRootDir =<< cradle + return $ parseCustomPackageDb <$> mCusPkgDbFile diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index 1d7d2d2..ef06b09 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -22,6 +22,7 @@ import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.CabalHelper import Language.Haskell.GhcMod.PathsAndFiles +import Language.Haskell.GhcMod.CustomPackageDb ghcVersion :: Int ghcVersion = read cProjectVersionInt diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index ca80bbd..47dc090 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -264,8 +264,9 @@ pkgDbStackCacheFile :: FilePath -> FilePath pkgDbStackCacheFile dist = setupConfigPath dist <.> "ghc-mod.package-db-stack" --- | @findCustomPackageDbFile dir@. Searches for a @.ghc-mod.cradle@ file in @dir@. --- If it exists in the given directory it is returned otherwise @findCradleFile@ returns @Nothing@ +-- | @findCustomPackageDbFile dir@. Searches for a @ghc-mod.package-db-stack@ file in @dir@. +-- If it exists in the given directory it is returned otherwise @findCradleFile@ +-- returns @Nothing@ findCustomPackageDbFile :: FilePath -> IO (Maybe FilePath) findCustomPackageDbFile directory = do let path = directory "ghc-mod.package-db-stack" diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index f1072ca..428b776 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -37,6 +37,7 @@ import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils as U import Language.Haskell.GhcMod.FileMapping import Language.Haskell.GhcMod.LightGhc +import Language.Haskell.GhcMod.CustomPackageDb import Data.Maybe import Data.Monoid as Monoid @@ -270,10 +271,11 @@ packageGhcOptions = do _ -> sandboxOpts crdl -- also works for plain projects! -sandboxOpts :: MonadIO m => Cradle -> m [String] +sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String] sandboxOpts crdl = do + mCusPkgDb <- getCustomPkgDbStack pkgDbStack <- liftIO $ getSandboxPackageDbStack - let pkgOpts = ghcDbStackOpts pkgDbStack + let pkgOpts = ghcDbStackOpts $ fromMaybe pkgDbStack mCusPkgDb return $ ["-i" ++ d | d <- [wdir,rdir]] ++ pkgOpts ++ ["-Wall"] where (wdir, rdir) = (cradleCurrentDir crdl, cradleRootDir crdl) diff --git a/README.md b/README.md index 28b1b3e..b06463a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ all sorts of nasty conflicts. ## Custom ghc-mod cradle -To customize the package databases used by `ghc-mod`, put a file called `ghc-mod.cradle` beside the `.cabal` file with the following syntax: +To customize the package databases used by `ghc-mod`, put a file called `ghc-mod.package-db-stack` beside the `.cabal` file with the following syntax: ``` temp directory root diff --git a/ghc-mod.cabal b/ghc-mod.cabal index a403a26..20347be 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -110,6 +110,7 @@ Library Language.Haskell.GhcMod.Check Language.Haskell.GhcMod.Convert Language.Haskell.GhcMod.Cradle + Language.Haskell.GhcMod.CustomPackageDb Language.Haskell.GhcMod.Debug Language.Haskell.GhcMod.Doc Language.Haskell.GhcMod.DynFlags From 026b64ee26328fe767ce9d112e096a7593d5f31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 7 Sep 2015 10:20:41 +0200 Subject: [PATCH 106/147] Update cabal-helper in stack.yaml --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index e25c8d2..964834d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -2,5 +2,5 @@ flags: {} packages: - '.' extra-deps: -- cabal-helper-0.5.1.0 +- cabal-helper-0.6.0.0 resolver: lts-3.1 From f06511bff147af9fcd2e0d453666302cfc82cec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 03:54:29 +0200 Subject: [PATCH 107/147] Support multi-package stack projects --- Language/Haskell/GhcMod/CabalHelper.hs | 4 ++-- Language/Haskell/GhcMod/PathsAndFiles.hs | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index 0fa2fee..af85eff 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -242,8 +242,8 @@ withCabal action = do supported <- haveStackSupport if supported then do - spawn [T.stackProgram progs, "build", "--only-dependencies"] - spawn [T.stackProgram progs, "build", "--only-configure"] + spawn [T.stackProgram progs, "build", "--only-dependencies", "."] + spawn [T.stackProgram progs, "build", "--only-configure", "."] writeAutogen projdir distdir else gmLog GmWarning "" $ strDoc $ "Stack project configuration is out of date, please reconfigure manually using 'stack build' as your stack version is too old (need at least 0.1.4.0)" diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 47dc090..13649c0 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -20,6 +20,7 @@ module Language.Haskell.GhcMod.PathsAndFiles ( ) where import Config (cProjectVersion) +import Control.Arrow (second) import Control.Applicative import Control.Exception as E import Control.Monad @@ -77,7 +78,12 @@ findCabalFile dir = do appendDir d fs = (d ) `map` fs findStackConfigFile :: FilePath -> IO (Maybe FilePath) -findStackConfigFile dir = mightExist (dir "stack.yaml") +findStackConfigFile dir = do + fs <- map (second listToMaybe) <$> findFileInParentsP (=="stack.yaml") dir + case find (isJust . snd) fs of + Nothing -> return Nothing + Just (d, Just a) -> return $ Just $ d a + Just (_, Nothing) -> error "findStackConfigFile" getStackDistDir :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do @@ -165,7 +171,7 @@ takeExtension' p = -- it's parent directories. findFileInParentsP :: (FilePath -> Bool) -> FilePath -> IO [(DirPath, [FileName])] -findFileInParentsP p dir = +findFileInParentsP p dir' = makeAbsolute dir' >>= \dir -> getFilesP p `zipMapM` parents dir -- | @getFilesP p dir@. Find all __files__ satisfying @p@ in @.cabal@ in @dir@. From d400c8f38958e413e83773e41a9c84a2db7dff48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 05:20:26 +0200 Subject: [PATCH 108/147] Fix missing makeAbsolute --- Language/Haskell/GhcMod/PathsAndFiles.hs | 2 +- Language/Haskell/GhcMod/Utils.hs | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 13649c0..228cc87 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -171,7 +171,7 @@ takeExtension' p = -- it's parent directories. findFileInParentsP :: (FilePath -> Bool) -> FilePath -> IO [(DirPath, [FileName])] -findFileInParentsP p dir' = makeAbsolute dir' >>= \dir -> +findFileInParentsP p dir' = U.makeAbsolute' dir' >>= \dir -> getFilesP p `zipMapM` parents dir -- | @getFilesP p dir@. Find all __files__ satisfying @p@ in @.cabal@ in @dir@. diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index d4f8043..236f608 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -29,16 +29,13 @@ import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Either (rights) import Data.List (inits) -import System.FilePath (joinPath, splitPath, normalise) import Exception import Language.Haskell.GhcMod.Error import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Monad.Types -import System.Directory (getCurrentDirectory, setCurrentDirectory, doesFileExist, - getTemporaryDirectory, canonicalizePath) +import System.Directory import System.Environment -import System.FilePath (splitDrive, takeDirectory, takeFileName, pathSeparators, - (), makeRelative) +import System.FilePath import System.IO.Temp (createTempDirectory) import System.Process (readProcess) import Text.Printf @@ -208,3 +205,17 @@ findFilesWith' f (d:ds) fileName = do files <- findFilesWith' f ds fileName return $ file : files else findFilesWith' f ds fileName + + +-- Copyright : (c) The University of Glasgow 2001 +-- | Make a path absolute by prepending the current directory (if it isn't +-- already absolute) and applying 'normalise' to the result. +-- +-- If the path is already absolute, the operation never fails. Otherwise, the +-- operation may fail with the same exceptions as 'getCurrentDirectory'. +makeAbsolute' :: FilePath -> IO FilePath +makeAbsolute' = (normalise <$>) . absolutize + where absolutize path -- avoid the call to `getCurrentDirectory` if we can + | isRelative path = ( path) . addTrailingPathSeparator <$> + getCurrentDirectory + | otherwise = return path From 34fedd8ad3292de622ff3387d85668b01ccf8cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 06:19:58 +0200 Subject: [PATCH 109/147] Missing source file --- Language/Haskell/GhcMod/DebugLogger.hs | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Language/Haskell/GhcMod/DebugLogger.hs diff --git a/Language/Haskell/GhcMod/DebugLogger.hs b/Language/Haskell/GhcMod/DebugLogger.hs new file mode 100644 index 0000000..74b8a49 --- /dev/null +++ b/Language/Haskell/GhcMod/DebugLogger.hs @@ -0,0 +1,62 @@ +module Language.Haskell.GhcMod.DebugLogger where + + +import GHC +import FastString +import Pretty +import Outputable (SDoc, PprStyle, runSDoc, initSDocContext, blankLine) +import qualified Outputable +import ErrUtils +import DynFlags (LogAction) + +import Language.Haskell.GhcMod.Error +import Language.Haskell.GhcMod.Output +import Language.Haskell.GhcMod.Monad.Types +import Prelude + +debugLogAction :: (String -> IO ()) -> LogAction +debugLogAction putErr dflags severity srcSpan style msg + = case severity of + SevOutput -> printSDoc msg style + SevDump -> printSDoc (msg Outputable.$$ blankLine) style + SevInteractive -> putStrSDoc msg style + SevInfo -> printErrs msg style + SevFatal -> printErrs msg style + _ -> do putErr "\n" + printErrs (mkLocMessage severity srcSpan msg) style + -- careful (#2302): printErrs prints in UTF-8, + -- whereas converting to string first and using + -- hPutStr would just emit the low 8 bits of + -- each unicode char. + where printSDoc = debugLogActionHPrintDoc dflags putErr + printErrs = debugLogActionHPrintDoc dflags putErr + putStrSDoc = debugLogActionHPutStrDoc dflags putErr + +debugLogActionHPrintDoc :: DynFlags -> (String -> IO ()) -> SDoc -> PprStyle -> IO () +debugLogActionHPrintDoc dflags put d sty + = debugLogActionHPutStrDoc dflags put (d Outputable.$$ Outputable.text "") sty + -- Adds a newline + +debugLogActionHPutStrDoc :: DynFlags -> (String -> IO ()) -> SDoc -> PprStyle -> IO () +debugLogActionHPutStrDoc dflags put d sty + = gmPrintDoc_ Pretty.PageMode (pprCols dflags) put doc + where -- Don't add a newline at the end, so that successive + -- calls to this log-action can output all on the same line + doc = runSDoc d (initSDocContext dflags sty) + + +gmPrintDoc :: Mode -> Int -> (String -> IO ()) -> Doc -> IO () +-- printDoc adds a newline to the end +gmPrintDoc mode cols put doc = gmPrintDoc_ mode cols put (doc $$ text "") + +gmPrintDoc_ :: Mode -> Int -> (String -> IO ()) -> Doc -> IO () +gmPrintDoc_ mode pprCols putS doc + = fullRender mode pprCols 1.5 put done doc + where + put (Chr c) next = putS [c] >> next + put (Str s) next = putS s >> next + put (PStr s) next = putS (unpackFS s) >> next + put (ZStr s) next = putS (zString s) >> next + put (LStr s _l) next = putS (unpackLitString s) >> next + + done = return () -- hPutChar hdl '\n' From 7ae22a92263bf2bf9e483ecd9d75231b6de53d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 06:20:12 +0200 Subject: [PATCH 110/147] Fix tests --- ghc-mod.cabal | 1 + test/CabalHelperSpec.hs | 22 ---------------------- test/CustomPackageDbSpec.hs | 35 +++++++++++++++++++++++++++++++++++ test/GhcPkgSpec.hs | 1 + test/PathsAndFilesSpec.hs | 13 +++++++++---- 5 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 test/CustomPackageDbSpec.hs diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 20347be..e6b10ee 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -243,6 +243,7 @@ Test-Suite spec Spec TestUtils BrowseSpec + CustomPackageDbSpec CheckSpec FlagSpec InfoSpec diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index c69bbab..162675e 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -79,25 +79,3 @@ spec = do let ghcOpts = head opts pkgs = pkgOptions ghcOpts pkgs `shouldBe` ["Cabal","base"] - - describe "getCustomPkgDbStack" $ do - it "works" $ do - let tdir = "test/data/custom-cradle" - Just stack <- runD' tdir $ getCustomPkgDbStack - stack `shouldBe` [ GlobalDb - , UserDb - , PackageDb "package-db-a" - , PackageDb "package-db-b" - , PackageDb "package-db-c" - ] - - describe "getPackageDbStack'" $ do - it "fixes out of sync custom pkg-db stack" $ do - withDirectory_ "test/data/custom-cradle" $ do - _ <- system "cabal configure" - (s, s') <- runD $ do - Just stack <- getCustomPkgDbStack - withCabal $ do - stack' <- getCabalPackageDbStack - return (stack, stack') - s' `shouldBe` s diff --git a/test/CustomPackageDbSpec.hs b/test/CustomPackageDbSpec.hs new file mode 100644 index 0000000..c19c193 --- /dev/null +++ b/test/CustomPackageDbSpec.hs @@ -0,0 +1,35 @@ +module CustomPackageDbSpec where + +import Language.Haskell.GhcMod.CabalHelper +import Language.Haskell.GhcMod.CustomPackageDb +import Language.Haskell.GhcMod.Error +import System.Process +import Test.Hspec +import Prelude + +import Dir +import TestUtils + +spec :: Spec +spec = do + describe "getCustomPkgDbStack" $ do + it "works" $ do + let tdir = "test/data/custom-cradle" + Just stack <- runD' tdir $ getCustomPkgDbStack + stack `shouldBe` [ GlobalDb + , UserDb + , PackageDb "package-db-a" + , PackageDb "package-db-b" + , PackageDb "package-db-c" + ] + + describe "getPackageDbStack'" $ do + it "fixes out of sync custom pkg-db stack" $ do + withDirectory_ "test/data/custom-cradle" $ do + _ <- system "cabal configure" + (s, s') <- runD $ do + Just stack <- getCustomPkgDbStack + withCabal $ do + stack' <- getCabalPackageDbStack + return (stack, stack') + s' `shouldBe` s diff --git a/test/GhcPkgSpec.hs b/test/GhcPkgSpec.hs index 768f2e4..69d9661 100644 --- a/test/GhcPkgSpec.hs +++ b/test/GhcPkgSpec.hs @@ -2,6 +2,7 @@ module GhcPkgSpec where import Language.Haskell.GhcMod.GhcPkg import Language.Haskell.GhcMod.CabalHelper +import Language.Haskell.GhcMod.CustomPackageDb import Test.Hspec import System.Process (system) diff --git a/test/PathsAndFilesSpec.hs b/test/PathsAndFilesSpec.hs index cb34aa4..ec3057b 100644 --- a/test/PathsAndFilesSpec.hs +++ b/test/PathsAndFilesSpec.hs @@ -3,6 +3,7 @@ module PathsAndFilesSpec where import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Cradle +import qualified Language.Haskell.GhcMod.Utils as U import Control.Monad.Trans.Maybe import System.Directory @@ -25,10 +26,12 @@ spec = do describe "findCabalFile" $ do it "works" $ do - findCabalFile "test/data/cabal-project" `shouldReturn` Just "test/data/cabal-project/cabalapi.cabal" + p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal" + findCabalFile "test/data/cabal-project" `shouldReturn` Just p it "finds cabal files in parent directories" $ do - findCabalFile "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just "test/data/cabal-project/cabalapi.cabal" + p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal" + findCabalFile "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just p describe "findStackConfigFile" $ do it "works" $ do @@ -36,7 +39,9 @@ spec = do describe "findCabalSandboxDir" $ do it "works" $ do - findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just "test/data/cabal-project" + p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal" + findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just p it "finds sandboxes in parent directories" $ do - findCabalSandboxDir "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just "test/data/cabal-project" + p <- U.makeAbsolute' "test/data/cabal-project/" + findCabalSandboxDir "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just p From dbf215a35b81e65915b2a2142ad17ba19cf6081c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 06:42:32 +0200 Subject: [PATCH 111/147] Fix tests, this time, this time for sure. --- Language/Haskell/GhcMod/Utils.hs | 3 +-- test/CabalHelperSpec.hs | 1 - test/PathsAndFilesSpec.hs | 7 ++++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/Utils.hs b/Language/Haskell/GhcMod/Utils.hs index 236f608..99d4190 100644 --- a/Language/Haskell/GhcMod/Utils.hs +++ b/Language/Haskell/GhcMod/Utils.hs @@ -216,6 +216,5 @@ findFilesWith' f (d:ds) fileName = do makeAbsolute' :: FilePath -> IO FilePath makeAbsolute' = (normalise <$>) . absolutize where absolutize path -- avoid the call to `getCurrentDirectory` if we can - | isRelative path = ( path) . addTrailingPathSeparator <$> - getCurrentDirectory + | isRelative path = ( path) <$> getCurrentDirectory | otherwise = return path diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 162675e..30bcfb0 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -9,7 +9,6 @@ import Language.Haskell.GhcMod.Error import Test.Hspec import System.Directory import System.FilePath -import System.Process (readProcess, system) import Prelude import Dir diff --git a/test/PathsAndFilesSpec.hs b/test/PathsAndFilesSpec.hs index ec3057b..b2ac6e6 100644 --- a/test/PathsAndFilesSpec.hs +++ b/test/PathsAndFilesSpec.hs @@ -35,13 +35,14 @@ spec = do describe "findStackConfigFile" $ do it "works" $ do - findStackConfigFile "test/data/stack-project" `shouldReturn` Just "test/data/stack-project/stack.yaml" + p <- U.makeAbsolute' "test/data/stack-project/stack.yaml" + findStackConfigFile "test/data/stack-project" `shouldReturn` Just p describe "findCabalSandboxDir" $ do it "works" $ do - p <- U.makeAbsolute' "test/data/cabal-project/cabalapi.cabal" + p <- U.makeAbsolute' "test/data/cabal-project" findCabalSandboxDir "test/data/cabal-project" `shouldReturn` Just p it "finds sandboxes in parent directories" $ do - p <- U.makeAbsolute' "test/data/cabal-project/" + p <- U.makeAbsolute' "test/data/cabal-project" findCabalSandboxDir "test/data/cabal-project/subdir1/subdir2" `shouldReturn` Just p From 6ad71af00157493b1378ddbd6046345689f19fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 8 Sep 2015 06:44:02 +0200 Subject: [PATCH 112/147] Don't swallow ghc's verbose output --- Language/Haskell/GhcMod/DynFlags.hs | 11 ++++++----- Language/Haskell/GhcMod/Output.hs | 22 +++++++++++++++++----- Language/Haskell/GhcMod/Target.hs | 4 +++- ghc-mod.cabal | 1 + 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index 796dc77..565c3ed 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -3,18 +3,19 @@ module Language.Haskell.GhcMod.DynFlags where import Control.Applicative -import Control.Monad (void) -import GHC (DynFlags(..), GhcMode(..), GhcLink(..), HscTarget(..)) +import Control.Monad +import GHC import qualified GHC as G import GHC.Paths (libdir) -import GhcMonad import qualified Language.Haskell.GhcMod.Gap as Gap import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.DebugLogger import System.IO.Unsafe (unsafePerformIO) import Prelude -setEmptyLogger :: DynFlags -> DynFlags -setEmptyLogger df = Gap.setLogAction df $ \_ _ _ _ _ -> return () +setDebugLogger :: (String -> IO ()) -> DynFlags -> DynFlags +setDebugLogger put df = do + Gap.setLogAction df (debugLogAction put) -- * Fast -- * Friendly to foreign export diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 7fcf0af..8503861 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -22,9 +22,15 @@ module Language.Haskell.GhcMod.Output ( , gmErrStr , gmPutStrLn , gmErrStrLn + + , gmPutStrIO + , gmErrStrIO + , gmReadProcess + , gmUnsafePutStr , gmUnsafeErrStr + , stdoutGateway ) where @@ -105,15 +111,21 @@ gmPutStr, gmPutStrLn, gmErrStr, gmErrStrLn :: (MonadIO m, GmOut m) => String -> m () gmPutStr str = do - putOut <- fst `liftM` outputFns - putOut $ toGmLines str + putOut <- gmPutStrIO + putOut str + +gmErrStr str = do + putErr <- gmErrStrIO + putErr str gmPutStrLn = gmPutStr . (++"\n") gmErrStrLn = gmErrStr . (++"\n") -gmErrStr str = do - putErr <- snd `liftM` outputFns - putErr $ toGmLines str +gmPutStrIO, gmErrStrIO :: (GmOut m, MonadIO mi) => m (String -> mi ()) + +gmPutStrIO = ((. toGmLines) . fst) `liftM` outputFns +gmErrStrIO = ((. toGmLines) . snd) `liftM` outputFns + -- | Only use these when you're sure there are no other writers on stdout gmUnsafePutStr, gmUnsafeErrStr diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 428b776..847433b 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -38,6 +38,7 @@ import Language.Haskell.GhcMod.Utils as U import Language.Haskell.GhcMod.FileMapping import Language.Haskell.GhcMod.LightGhc import Language.Haskell.GhcMod.CustomPackageDb +import Language.Haskell.GhcMod.Output import Data.Maybe import Data.Monoid as Monoid @@ -131,8 +132,9 @@ runGmlTWith efnmns' mdf wrapper action = do (text "Initializing GHC session with following options") (intercalate " " $ map (("\""++) . (++"\"")) opts') + putErr <- gmErrStrIO initSession opts' $ - setModeSimple >>> setEmptyLogger >>> mdf + setModeSimple >>> setDebugLogger putErr >>> mdf mappedStrs <- getMMappedFilePaths let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns diff --git a/ghc-mod.cabal b/ghc-mod.cabal index e6b10ee..2d62ac1 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -112,6 +112,7 @@ Library Language.Haskell.GhcMod.Cradle Language.Haskell.GhcMod.CustomPackageDb Language.Haskell.GhcMod.Debug + Language.Haskell.GhcMod.DebugLogger Language.Haskell.GhcMod.Doc Language.Haskell.GhcMod.DynFlags Language.Haskell.GhcMod.Error From e92bd0af485d9f5d40e40ed282f552bf99c6c87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Thu, 10 Sep 2015 07:48:13 +0200 Subject: [PATCH 113/147] Disable obj loading for targets (Fix #554) --- Language/Haskell/GhcMod/Gap.hs | 17 +++++++---------- Language/Haskell/GhcMod/Target.hs | 4 +++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index cbf773b..df9cb77 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -223,22 +223,19 @@ withInteractiveContext action = gbracket setup teardown body topImports >>= setCtx action topImports = do - mss <- getModuleGraph - mns <- map modName <$> filterM isTop mss - let ii = map IIModule mns + ms <- filterM moduleIsInterpreted =<< map ms_mod <$> getModuleGraph + liftIO $ print (map modName ms) + let iis = map (IIModule . modName) ms #if __GLASGOW_HASKELL__ >= 704 - return ii + return iis #else - return (ii,[]) + return (iis,[]) #endif - isTop mos = lookupMod mos ||> returnFalse - lookupMod mos = lookupModule (ms_mod_name mos) Nothing >> return True - returnFalse = return False #if __GLASGOW_HASKELL__ >= 706 - modName = moduleName . ms_mod + modName = moduleName setCtx = setContext #elif __GLASGOW_HASKELL__ >= 704 - modName = ms_mod + modName = id setCtx = setContext #else modName = ms_mod diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 847433b..8fb0336 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -409,12 +409,14 @@ resolveGmComponents mumns cs = do -- | Set the files as targets and load them. loadTargets :: IOish m => [GHCOption] -> [FilePath] -> GmlT m () loadTargets opts targetStrs = do - targets <- + targets' <- withLightHscEnv opts $ \env -> liftM (nubBy ((==) `on` targetId)) (mapM ((`guessTarget` Nothing) >=> mapFile env) targetStrs) >>= mapM relativize + let targets = map (\t -> t { targetAllowObjCode = False }) targets' + gmLog GmDebug "loadTargets" $ text "Loading" <+>: fsep (map (text . showTargetId) targets) From 8413f0a1fe52f92574d24a253f6d49fb8cd78fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Thu, 10 Sep 2015 07:57:29 +0200 Subject: [PATCH 114/147] Fix tests --- test/CabalHelperSpec.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/CabalHelperSpec.hs b/test/CabalHelperSpec.hs index 30bcfb0..a400fc6 100644 --- a/test/CabalHelperSpec.hs +++ b/test/CabalHelperSpec.hs @@ -9,6 +9,7 @@ import Language.Haskell.GhcMod.Error import Test.Hspec import System.Directory import System.FilePath +import System.Process import Prelude import Dir From 12d65ba11f57e77155dcac8cbb5f093f392b6d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Sep 2015 03:52:20 +0200 Subject: [PATCH 115/147] Fix some warnings --- Language/Haskell/GhcMod/DebugLogger.hs | 2 -- Language/Haskell/GhcMod/Gap.hs | 4 ---- 2 files changed, 6 deletions(-) diff --git a/Language/Haskell/GhcMod/DebugLogger.hs b/Language/Haskell/GhcMod/DebugLogger.hs index 74b8a49..3e3069a 100644 --- a/Language/Haskell/GhcMod/DebugLogger.hs +++ b/Language/Haskell/GhcMod/DebugLogger.hs @@ -10,8 +10,6 @@ import ErrUtils import DynFlags (LogAction) import Language.Haskell.GhcMod.Error -import Language.Haskell.GhcMod.Output -import Language.Haskell.GhcMod.Monad.Types import Prelude debugLogAction :: (String -> IO ()) -> LogAction diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index df9cb77..5773515 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -242,10 +242,6 @@ withInteractiveContext action = gbracket setup teardown body setCtx = uncurry setContext #endif --- | Try the left action, if an IOException occurs try the right action. -(||>) :: ExceptionMonad m => m a -> m a -> m a -x ||> y = x `gcatch` (\(_ :: IOException) -> y) - showSeverityCaption :: Severity -> String #if __GLASGOW_HASKELL__ >= 706 showSeverityCaption SevWarning = "Warning: " From 211b957451ad9cc98d1ed1937e585846d7f75605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Sep 2015 03:48:52 +0200 Subject: [PATCH 116/147] Fix race condition in stack support code --- Language/Haskell/GhcMod.hs | 2 +- Language/Haskell/GhcMod/CabalHelper.hs | 32 +++++++----------------- Language/Haskell/GhcMod/Cradle.hs | 12 ++++----- Language/Haskell/GhcMod/Debug.hs | 12 ++++----- Language/Haskell/GhcMod/GhcPkg.hs | 12 ++++----- Language/Haskell/GhcMod/PathsAndFiles.hs | 30 ++++++++++++++-------- Language/Haskell/GhcMod/Target.hs | 16 ++++++------ Language/Haskell/GhcMod/Types.hs | 21 +++++++++++++--- src/GHCMod.hs | 2 +- 9 files changed, 74 insertions(+), 65 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index 9ee6180..4cf0b38 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -3,7 +3,7 @@ module Language.Haskell.GhcMod ( -- * Cradle Cradle(..) - , ProjectType(..) + , Project(..) , findCradle -- * Options , Options(..) diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index af85eff..f9db18b 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -20,7 +20,6 @@ module Language.Haskell.GhcMod.CabalHelper ( getComponents , getGhcMergedPkgOptions , getCabalPackageDbStack - , getStackPackageDbStack , prepareCabalHelper ) #endif @@ -44,7 +43,6 @@ import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Output import Language.Haskell.GhcMod.CustomPackageDb import System.FilePath -import System.Directory (findExecutable) import System.Process import System.Exit import Prelude hiding ((.)) @@ -136,30 +134,18 @@ prepareCabalHelper = do let projdir = cradleRootDir crdl distdir = projdir cradleDistDir crdl readProc <- gmReadProcess - when (cradleProjectType crdl == CabalProject || cradleProjectType crdl == StackProject) $ + when (isCabalHelperProject $ cradleProject crdl) $ withCabal $ liftIO $ prepare readProc projdir distdir -getStackPackageDbStack :: IOish m => m [GhcPkgDb] -getStackPackageDbStack = do - mstack <- liftIO $ findExecutable "stack" - case mstack of - Nothing -> return [] - Just stack -> do - snapshotDb <- liftIO $ readProcess stack ["path", "--snapshot-pkg-db"] "" - localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] "" - return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb] - patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs -patchStackPrograms crdl progs - | cradleProjectType crdl /= StackProject = return progs -patchStackPrograms crdl progs = do - let projdir = cradleRootDir crdl - Just ghc <- getStackGhcPath projdir - Just ghcPkg <- getStackGhcPkgPath projdir +patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do + Just ghc <- getStackGhcPath senv + Just ghcPkg <- getStackGhcPkgPath senv return $ progs { ghcProgram = ghc , ghcPkgProgram = ghcPkg } +patchStackPrograms _crdl progs = return progs withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a withCabal action = do @@ -188,7 +174,7 @@ withCabal action = do Nothing -> return False - projType <- cradleProjectType <$> cradle + proj <- cradleProject <$> cradle when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $ gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project." @@ -202,14 +188,14 @@ withCabal action = do when ( isSetupConfigOutOfDate mCabalFile mCabalConfig || pkgDbStackOutOfSync || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ - case projType of + case proj of CabalProject -> cabalReconfigure readProc (optPrograms opts) crdl projdir distdir - StackProject -> + StackProject {} -> stackReconfigure crdl (optPrograms opts) _ -> - error $ "withCabal: unsupported project type: " ++ show projType + error $ "withCabal: unsupported project type: " ++ show proj action diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index aa2d082..45ef00d 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -69,7 +69,7 @@ cabalCradle wdir = do let cabalDir = takeDirectory cabalFile return Cradle { - cradleProjectType = CabalProject + cradleProject = CabalProject , cradleCurrentDir = wdir , cradleRootDir = cabalDir , cradleTempDir = error "tmpDir" @@ -89,22 +89,22 @@ stackCradle wdir = do -- rather than stack, or maybe that's just me ;) whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero - distDir <- MaybeT $ getStackDistDir cabalDir + senv <- MaybeT $ getStackEnv cabalDir return Cradle { - cradleProjectType = StackProject + cradleProject = StackProject senv , cradleCurrentDir = wdir , cradleRootDir = cabalDir , cradleTempDir = error "tmpDir" , cradleCabalFile = Just cabalFile - , cradleDistDir = distDir + , cradleDistDir = seDistDir senv } sandboxCradle :: IOish m => FilePath -> MaybeT m Cradle sandboxCradle wdir = do sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir return Cradle { - cradleProjectType = SandboxProject + cradleProject = SandboxProject , cradleCurrentDir = wdir , cradleRootDir = sbDir , cradleTempDir = error "tmpDir" @@ -115,7 +115,7 @@ sandboxCradle wdir = do plainCradle :: IOish m => FilePath -> MaybeT m Cradle plainCradle wdir = do return $ Cradle { - cradleProjectType = PlainProject + cradleProject = PlainProject , cradleCurrentDir = wdir , cradleRootDir = wdir , cradleTempDir = error "tmpDir" diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index 2d363a8..bb7b7ac 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -26,9 +26,9 @@ debugInfo = do Cradle {..} <- cradle cabal <- - case cradleProjectType of + case cradleProject of CabalProject -> cabalDebug - StackProject -> (++) <$> stackPaths <*> cabalDebug + StackProject {} -> (++) <$> stackPaths <*> cabalDebug _ -> return [] pkgOpts <- packageGhcOptions @@ -45,9 +45,9 @@ debugInfo = do stackPaths :: IOish m => GhcModT m [String] stackPaths = do - Cradle {..} <- cradle - Just ghc <- getStackGhcPath cradleRootDir - Just ghcPkg <- getStackGhcPkgPath cradleRootDir + Cradle { cradleProject = StackProject senv } <- cradle + ghc <- getStackGhcPath senv + ghcPkg <- getStackGhcPkgPath senv return $ [ "Stack ghc executable: " ++ show ghc , "Stack ghc-pkg executable:" ++ show ghcPkg @@ -64,7 +64,7 @@ cabalDebug = do return $ [ "Cabal file: " ++ show cradleCabalFile - , "Cabal Project Type: " ++ show cradleProjectType + , "Project: " ++ show cradleProject , "Cabal entrypoints:\n" ++ render (nest 4 $ mapDoc gmComponentNameDoc smpDoc entrypoints) , "Cabal components:\n" ++ render (nest 4 $ diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index ef06b09..9bff334 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -65,9 +65,9 @@ getGhcPkgProgram :: IOish m => GhcModT m FilePath getGhcPkgProgram = do crdl <- cradle progs <- optPrograms <$> options - case cradleProjectType crdl of - StackProject -> do - Just ghcPkg <- getStackGhcPkgPath (cradleRootDir crdl) + case cradleProject crdl of + (StackProject senv) -> do + Just ghcPkg <- getStackGhcPkgPath senv return ghcPkg _ -> return $ ghcPkgProgram progs @@ -77,7 +77,7 @@ getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb] getPackageDbStack = do crdl <- cradle mCusPkgStack <- getCustomPkgDbStack - stack <- case cradleProjectType crdl of + stack <- case cradleProject crdl of PlainProject -> return [GlobalDb, UserDb] SandboxProject -> do @@ -85,8 +85,8 @@ getPackageDbStack = do return $ [GlobalDb, db] CabalProject -> getCabalPackageDbStack - StackProject -> - getStackPackageDbStack + (StackProject StackEnv {..}) -> + return $ map PackageDb [seSnapshotPkgDb, seLocalPkgDb] return $ fromMaybe stack mCusPkgStack getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath] diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 228cc87..959e2b6 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -27,6 +27,7 @@ import Control.Monad import Control.Monad.Trans.Maybe import Control.Monad.Trans.Class import Data.List +import Data.List.Split import Data.Char import Data.Maybe import Data.Traversable hiding (mapM) @@ -85,22 +86,29 @@ findStackConfigFile dir = do Just (d, Just a) -> return $ Just $ d a Just (_, Nothing) -> error "findStackConfigFile" -getStackDistDir :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) -getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do - takeWhile (/='\n') <$> readStack ["path", "--dist-dir"] +getStackEnv :: (IOish m, GmOut m) => FilePath -> m (Maybe StackEnv) +getStackEnv projdir = U.withDirectory_ projdir $ runMaybeT $ do + env <- map (liToTup . splitOn ": ") . lines <$> readStack ["path"] + let look k = fromJust $ lookup k env + return StackEnv { + seDistDir = look "dist-dir" + , seBinPath = splitSearchPath $ look "bin-path" + , seSnapshotPkgDb = look "snapshot-pkg-db" + , seLocalPkgDb = look "local-pkg-db" + } + where + liToTup [k,v] = (k,v) + liToTup _ = error "getStackEnv" -getStackGhcPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) +getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath) getStackGhcPath = findExecutablesInStackBinPath "ghc" -getStackGhcPkgPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) +getStackGhcPkgPath :: IOish m => StackEnv -> m (Maybe FilePath) getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" -findExecutablesInStackBinPath :: (IOish m, GmOut m) => String -> FilePath -> m (Maybe FilePath) -findExecutablesInStackBinPath exe projdir = - U.withDirectory_ projdir $ runMaybeT $ do - path <- splitSearchPath . takeWhile (/='\n') - <$> readStack ["path", "--bin-path"] - MaybeT $ liftIO $ listToMaybe <$> findExecutablesInDirectories' path exe +findExecutablesInStackBinPath :: IOish m => String -> StackEnv -> m (Maybe FilePath) +findExecutablesInStackBinPath exe StackEnv {..} = + liftIO $ listToMaybe <$> findExecutablesInDirectories' seBinPath exe findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] findExecutablesInDirectories' path binary = diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 8fb0336..3906b0a 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -150,10 +150,10 @@ targetGhcOptions :: forall m. IOish m targetGhcOptions crdl sefnmn = do when (Set.null sefnmn) $ error "targetGhcOptions: no targets given" - case cradleProjectType crdl of - CabalProject -> cabalOpts crdl - StackProject -> cabalOpts crdl - _ -> sandboxOpts crdl + case cradleProject crdl of + proj + | isCabalHelperProject proj -> cabalOpts crdl + | otherwise -> sandboxOpts crdl where zipMap f l = l `zip` (f `map` l) @@ -267,10 +267,10 @@ packageGhcOptions :: (Applicative m, IOish m, Gm m) => m [GHCOption] packageGhcOptions = do crdl <- cradle - case cradleProjectType crdl of - CabalProject -> getGhcMergedPkgOptions - StackProject -> getGhcMergedPkgOptions - _ -> sandboxOpts crdl + case cradleProject crdl of + proj + | isCabalHelperProject proj -> getGhcMergedPkgOptions + | otherwise -> sandboxOpts crdl -- also works for plain projects! sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String] diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 3043011..473c56e 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -139,12 +139,27 @@ defaultOptions = Options { ---------------------------------------------------------------- -data ProjectType = CabalProject | SandboxProject | PlainProject | StackProject - deriving (Eq, Show) +data Project = CabalProject + | SandboxProject + | PlainProject + | StackProject StackEnv + deriving (Eq, Show) + +isCabalHelperProject :: Project -> Bool +isCabalHelperProject StackProject {} = True +isCabalHelperProject CabalProject {} = True +isCabalHelperProject _ = False + +data StackEnv = StackEnv { + seDistDir :: FilePath + , seBinPath :: [FilePath] + , seSnapshotPkgDb :: FilePath + , seLocalPkgDb :: FilePath + } deriving (Eq, Show) -- | The environment where this library is used. data Cradle = Cradle { - cradleProjectType:: ProjectType + cradleProject :: Project -- | The directory where this library is executed. , cradleCurrentDir :: FilePath -- | The project root directory. diff --git a/src/GHCMod.hs b/src/GHCMod.hs index 05961d6..ce8b75b 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -679,7 +679,7 @@ nukeCaches = do chdir <- liftIO $ ( "cabal-helper") <$> getAppUserDataDirectory "ghc-mod" c <- cradle - when (cradleProjectType c == CabalProject || cradleProjectType c == StackProject) $ do + when (isCabalHelperProject $ cradleProject c) $ do let root = cradleRootDir c let dist = cradleDistDir c liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root dist] From acf78f25008e2b5f686d3899be9f27c81d5244d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Sep 2015 03:57:54 +0200 Subject: [PATCH 117/147] Remove stray debug code --- Language/Haskell/GhcMod/Gap.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index 5773515..da96c82 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -224,7 +224,6 @@ withInteractiveContext action = gbracket setup teardown body action topImports = do ms <- filterM moduleIsInterpreted =<< map ms_mod <$> getModuleGraph - liftIO $ print (map modName ms) let iis = map (IIModule . modName) ms #if __GLASGOW_HASKELL__ >= 704 return iis From ce1d9d1da1e306f2aed34f3e4d5d02daab8c2d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Sep 2015 04:13:44 +0200 Subject: [PATCH 118/147] Fix ghc<7.10 --- Language/Haskell/GhcMod/DebugLogger.hs | 52 ++++++++++++++++++++------ Language/Haskell/GhcMod/Gap.hs | 11 ++++-- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Language/Haskell/GhcMod/DebugLogger.hs b/Language/Haskell/GhcMod/DebugLogger.hs index 3e3069a..83c11c9 100644 --- a/Language/Haskell/GhcMod/DebugLogger.hs +++ b/Language/Haskell/GhcMod/DebugLogger.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.DebugLogger where @@ -7,28 +8,46 @@ import Pretty import Outputable (SDoc, PprStyle, runSDoc, initSDocContext, blankLine) import qualified Outputable import ErrUtils -import DynFlags (LogAction) import Language.Haskell.GhcMod.Error +import Language.Haskell.GhcMod.Gap import Prelude -debugLogAction :: (String -> IO ()) -> LogAction +debugLogAction :: (String -> IO ()) -> GmLogAction debugLogAction putErr dflags severity srcSpan style msg = case severity of - SevOutput -> printSDoc msg style - SevDump -> printSDoc (msg Outputable.$$ blankLine) style - SevInteractive -> putStrSDoc msg style - SevInfo -> printErrs msg style - SevFatal -> printErrs msg style + SevOutput -> printSDoc putErr msg style + +#if __GLASGOW_HASKELL__ >= 706 + SevDump -> printSDoc putErr (msg Outputable.$$ blankLine) style +#endif + +#if __GLASGOW_HASKELL__ >= 708 + SevInteractive -> let + putStrSDoc = debugLogActionHPutStrDoc dflags putErr + in + putStrSDoc msg style +#endif + SevInfo -> printErrs putErr msg style + SevFatal -> printErrs putErr msg style _ -> do putErr "\n" - printErrs (mkLocMessage severity srcSpan msg) style +#if __GLASGOW_HASKELL__ >= 706 + printErrs putErr (mkLocMessage severity srcSpan msg) style +#else + printErrs putErr (mkLocMessage srcSpan msg) style +#endif -- careful (#2302): printErrs prints in UTF-8, -- whereas converting to string first and using -- hPutStr would just emit the low 8 bits of -- each unicode char. - where printSDoc = debugLogActionHPrintDoc dflags putErr - printErrs = debugLogActionHPrintDoc dflags putErr - putStrSDoc = debugLogActionHPutStrDoc dflags putErr + where +#if __GLASGOW_HASKELL__ >= 706 + printSDoc put = debugLogActionHPrintDoc dflags put + printErrs put = debugLogActionHPrintDoc dflags put +#endif + + +#if __GLASGOW_HASKELL__ >= 706 debugLogActionHPrintDoc :: DynFlags -> (String -> IO ()) -> SDoc -> PprStyle -> IO () debugLogActionHPrintDoc dflags put d sty @@ -42,6 +61,15 @@ debugLogActionHPutStrDoc dflags put d sty -- calls to this log-action can output all on the same line doc = runSDoc d (initSDocContext dflags sty) +#else + +printSDoc = printErrs + +printErrs :: (String -> IO ()) -> SDoc -> PprStyle -> IO () +printErrs put doc sty = do + gmPrintDoc PageMode 100 put (runSDoc doc (initSDocContext sty)) + +#endif gmPrintDoc :: Mode -> Int -> (String -> IO ()) -> Doc -> IO () -- printDoc adds a newline to the end @@ -54,7 +82,9 @@ gmPrintDoc_ mode pprCols putS doc put (Chr c) next = putS [c] >> next put (Str s) next = putS s >> next put (PStr s) next = putS (unpackFS s) >> next +#if __GLASGOW_HASKELL__ >= 708 put (ZStr s) next = putS (zString s) >> next +#endif put (LStr s _l) next = putS (unpackLitString s) >> next done = return () -- hPutChar hdl '\n' diff --git a/Language/Haskell/GhcMod/Gap.hs b/Language/Haskell/GhcMod/Gap.hs index da96c82..00d8bc5 100644 --- a/Language/Haskell/GhcMod/Gap.hs +++ b/Language/Haskell/GhcMod/Gap.hs @@ -4,6 +4,7 @@ module Language.Haskell.GhcMod.Gap ( Language.Haskell.GhcMod.Gap.ClsInst , mkTarget , withStyle + , GmLogAction , setLogAction , getSrcSpan , getSrcFile @@ -135,9 +136,13 @@ withStyle = withPprStyleDoc withStyle _ = withPprStyleDoc #endif -setLogAction :: DynFlags - -> (DynFlags -> Severity -> SrcSpan -> PprStyle -> SDoc -> IO ()) - -> DynFlags +#if __GLASGOW_HASKELL__ >= 706 +type GmLogAction = LogAction +#else +type GmLogAction = DynFlags -> LogAction +#endif + +setLogAction :: DynFlags -> GmLogAction -> DynFlags setLogAction df f = #if __GLASGOW_HASKELL__ >= 706 df { log_action = f } From 64379a7c56f4e1d6efa44439ec9be8e644b43faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 11 Sep 2015 09:53:24 +0200 Subject: [PATCH 119/147] Actually update `world` in legacyInteractiveLoop --- Language/Haskell/GhcMod/Target.hs | 9 +++++++-- src/GHCMod.hs | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 3906b0a..79ccaef 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -69,8 +69,13 @@ initSession :: IOish m initSession opts mdf = do s <- gmsGet case gmGhcSession s of - Just GmGhcSession {..} -> when (gmgsOptions /= opts) $ putNewSession s - Nothing -> putNewSession s + Just GmGhcSession {..} | gmgsOptions /= opts-> do + gmLog GmDebug "initSession" $ text "Flags changed, creating new session" + putNewSession s + Just _ -> return () + Nothing -> do + gmLog GmDebug "initSession" $ text "Session not initialized, creating new one" + putNewSession s where putNewSession s = do diff --git a/src/GHCMod.hs b/src/GHCMod.hs index ce8b75b..a1a8ea3 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -462,6 +462,11 @@ legacyInteractiveLoop symdbreq world = do -- after blocking, we need to see if the world has changed. changed <- didWorldChange world + + world' <- if changed + then getCurrentWorld -- TODO: gah, we're hitting the fs twice + else return world + when changed $ do dropSession @@ -500,7 +505,7 @@ legacyInteractiveLoop symdbreq world = do _ -> fatalError $ "unknown command: `" ++ cmd ++ "'" gmPutStr res >> gmPutStrLn "OK" >> liftIO (hFlush stdout) - legacyInteractiveLoop symdbreq world + legacyInteractiveLoop symdbreq world' where interactiveHandlers = [ GHandler $ \e@(FatalError _) -> throw e From 7db740c2afc5808308c93f116d6f7e87f85e1792 Mon Sep 17 00:00:00 2001 From: Hiromi Ishii Date: Sun, 13 Sep 2015 16:42:48 +0900 Subject: [PATCH 120/147] Fixed #561 --- elisp/ghc-check.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index f795c58..181b62e 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -165,12 +165,17 @@ nil do not display errors/warnings. (err (ghc-hilit-info-get-err info)) (hole (ghc-hilit-info-get-hole info)) (coln (ghc-hilit-info-get-coln info)) + (is-same-file + (or (file-equal-p ofile file) + (string= (file-truename ofile) (file-truename file))) + ; In case non-existent + ) beg end ovl) ;; FIXME: This is the Shlemiel painter's algorithm. ;; If this is a bottleneck for a large code, let's fix. (goto-char (point-min)) (cond - ((string= (file-truename ofile) (file-truename file)) + (is-same-file (if hole (progn (forward-line (1- line)) From 74ba886fa185c2ed8b2c17dcb43805bd3590f049 Mon Sep 17 00:00:00 2001 From: Hiromi Ishii Date: Sun, 13 Sep 2015 18:20:11 +0900 Subject: [PATCH 121/147] Fixed variable binding miss --- elisp/ghc-check.el | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/elisp/ghc-check.el b/elisp/ghc-check.el index 181b62e..fe636ab 100644 --- a/elisp/ghc-check.el +++ b/elisp/ghc-check.el @@ -165,17 +165,12 @@ nil do not display errors/warnings. (err (ghc-hilit-info-get-err info)) (hole (ghc-hilit-info-get-hole info)) (coln (ghc-hilit-info-get-coln info)) - (is-same-file - (or (file-equal-p ofile file) - (string= (file-truename ofile) (file-truename file))) - ; In case non-existent - ) beg end ovl) ;; FIXME: This is the Shlemiel painter's algorithm. ;; If this is a bottleneck for a large code, let's fix. (goto-char (point-min)) (cond - (is-same-file + ((file-equal-p ofile file) (if hole (progn (forward-line (1- line)) From 4536ac545da0549978b9f445257fc9dab240b010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 05:40:07 +0200 Subject: [PATCH 122/147] Make sure stdoutGateway is running during findCradle' --- Language/Haskell/GhcMod/Monad.hs | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 0ecdbc7..76a74a5 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -26,7 +26,6 @@ module Language.Haskell.GhcMod.Monad ( , runGmlTWith , runGmPkgGhc , withGhcModEnv - , withGhcModEnv' , module Language.Haskell.GhcMod.Monad.Types ) where @@ -52,28 +51,23 @@ import Exception (ExceptionMonad(..)) import System.Directory import Prelude -withCradle :: (IOish m, GmOut m) => FilePath -> (Cradle -> m a) -> m a -withCradle cradledir f = - gbracket (findCradle' cradledir) (liftIO . cleanupCradle) f - withGhcModEnv :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a withGhcModEnv dir opts f = - withCradle dir (withGhcModEnv' opts f) - -withGhcModEnv' :: (IOish m, GmOut m) => Options -> (GhcModEnv -> m a) -> Cradle -> m a -withGhcModEnv' opts f crdl = do - olddir <- liftIO getCurrentDirectory - gbracket_ setup (teardown olddir) (f $ GhcModEnv opts crdl) + withStdoutGateway $ + withCradle $ \crdl -> + withCradleRootDir crdl $ + f $ GhcModEnv opts crdl where - setup = do - c <- gmoChan <$> gmoAsk - liftIO $ do - setCurrentDirectory $ cradleRootDir crdl - forkIO $ stdoutGateway c + withStdoutGateway a = do + c <- gmoChan <$> gmoAsk + gbracket_ (liftIO $ forkIO $ stdoutGateway c) (liftIO . killThread) a - teardown olddir tid = liftIO $ do - setCurrentDirectory olddir - killThread tid + withCradle = + gbracket (findCradle' dir) (liftIO . cleanupCradle) + + withCradleRootDir (cradleRootDir -> projdir) = + gbracket_ (liftIO $ setCurrentDirectory projdir >> getCurrentDirectory) + (liftIO . setCurrentDirectory) gbracket_ ma mb mc = gbracket ma mb (const mc) From ba14e1790c25fdd67a996780e55152812cecdacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 05:59:01 +0200 Subject: [PATCH 123/147] Fix tests --- Language/Haskell/GhcMod/Cradle.hs | 9 ++++----- Language/Haskell/GhcMod/Monad.hs | 14 +++++++++----- test/TestUtils.hs | 6 +++--- test/doctests.hs | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 45ef00d..5bd5c99 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -41,18 +41,17 @@ findCradle' dir = run $ ] where run a = fillTempDir =<< (fromJust <$> runMaybeT a) -findSpecCradle :: FilePath -> IO Cradle +findSpecCradle :: (IOish m, GmOut m) => FilePath -> m Cradle findSpecCradle dir = do - let cfs = [cabalCradle, sandboxCradle] + let cfs = [stackCradle, cabalCradle, sandboxCradle] cs <- catMaybes <$> mapM (runMaybeT . ($ dir)) cfs gcs <- filterM isNotGmCradle cs fillTempDir =<< case gcs of [] -> fromJust <$> runMaybeT (plainCradle dir) c:_ -> return c where - isNotGmCradle :: Cradle -> IO Bool - isNotGmCradle crdl = do - not <$> doesFileExist (cradleRootDir crdl "ghc-mod.cabal") + isNotGmCradle crdl = + liftIO $ not <$> doesFileExist (cradleRootDir crdl "ghc-mod.cabal") cleanupCradle :: Cradle -> IO () cleanupCradle crdl = removeDirectoryRecursive $ cradleTempDir crdl diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 76a74a5..7fe5a33 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -26,6 +26,7 @@ module Language.Haskell.GhcMod.Monad ( , runGmlTWith , runGmPkgGhc , withGhcModEnv + , withGhcModEnv' , module Language.Haskell.GhcMod.Monad.Types ) where @@ -52,9 +53,15 @@ import System.Directory import Prelude withGhcModEnv :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a -withGhcModEnv dir opts f = +withGhcModEnv = withGhcModEnv' withCradle + where + withCradle dir = + gbracket (findCradle' dir) (liftIO . cleanupCradle) + +withGhcModEnv' :: (IOish m, GmOut m) => (FilePath -> (Cradle -> m a) -> m a) -> FilePath -> Options -> (GhcModEnv -> m a) -> m a +withGhcModEnv' withCradle dir opts f = withStdoutGateway $ - withCradle $ \crdl -> + withCradle dir $ \crdl -> withCradleRootDir crdl $ f $ GhcModEnv opts crdl where @@ -62,9 +69,6 @@ withGhcModEnv dir opts f = c <- gmoChan <$> gmoAsk gbracket_ (liftIO $ forkIO $ stdoutGateway c) (liftIO . killThread) a - withCradle = - gbracket (findCradle' dir) (liftIO . cleanupCradle) - withCradleRootDir (cradleRootDir -> projdir) = gbracket_ (liftIO $ setCurrentDirectory projdir >> getCurrentDirectory) (liftIO . setCurrentDirectory) diff --git a/test/TestUtils.hs b/test/TestUtils.hs index d5a1429..13d4de0 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -44,12 +44,12 @@ extract action = do Right a -> return a Left e -> error $ show e -withSpecCradle :: IOish m => FilePath -> (Cradle -> m a) -> m a +withSpecCradle :: (IOish m, GmOut m) => FilePath -> (Cradle -> m a) -> m a withSpecCradle cradledir f = - gbracket (liftIO $ findSpecCradle cradledir) (liftIO . cleanupCradle) f + gbracket (findSpecCradle cradledir) (liftIO . cleanupCradle) f withGhcModEnvSpec :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a -withGhcModEnvSpec dir opt f = withSpecCradle dir $ withGhcModEnv' opt f +withGhcModEnvSpec = withGhcModEnv' withSpecCradle runGhcModTSpec :: Options -> GhcModT IO a -> IO (Either GhcModError a, GhcModLog) runGhcModTSpec opt action = do diff --git a/test/doctests.hs b/test/doctests.hs index 03d710f..6659d8c 100644 --- a/test/doctests.hs +++ b/test/doctests.hs @@ -9,7 +9,7 @@ main = doctest , "-package", "transformers-" ++ VERSION_transformers , "-package", "mtl-" ++ VERSION_mtl , "-package", "directory-" ++ VERSION_directory - , "-XScopedTypeVariables", "-XRecordWildCards", "-XNamedFieldPuns", "-XConstraintKinds", "-XFlexibleContexts", "-XDataKinds", "-XKindSignatures", "-XTypeOperators" + , "-XScopedTypeVariables", "-XRecordWildCards", "-XNamedFieldPuns", "-XConstraintKinds", "-XFlexibleContexts", "-XDataKinds", "-XKindSignatures", "-XTypeOperators", "-XViewPatterns" , "-idist/build/autogen/" , "-optP-include" , "-optPdist/build/autogen/cabal_macros.h" From 55f278853ab1699060770d5bfdc3790add562144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 07:11:45 +0200 Subject: [PATCH 124/147] Fix tests more --- Language/Haskell/GhcMod/Cradle.hs | 15 ++++++++++++++- Language/Haskell/GhcMod/Monad.hs | 11 ++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index 5bd5c99..c294d11 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -43,7 +43,7 @@ findCradle' dir = run $ findSpecCradle :: (IOish m, GmOut m) => FilePath -> m Cradle findSpecCradle dir = do - let cfs = [stackCradle, cabalCradle, sandboxCradle] + let cfs = [stackCradleSpec, cabalCradle, sandboxCradle] cs <- catMaybes <$> mapM (runMaybeT . ($ dir)) cfs gcs <- filterM isNotGmCradle cs fillTempDir =<< case gcs of @@ -99,6 +99,19 @@ stackCradle wdir = do , cradleDistDir = seDistDir senv } +stackCradleSpec :: (IOish m, GmOut m) => FilePath -> MaybeT m Cradle +stackCradleSpec wdir = do + crdl <- stackCradle wdir + case crdl of + Cradle { cradleProject = StackProject StackEnv { seDistDir } } -> do + b <- isGmDistDir seDistDir + when b mzero + return crdl + _ -> error "stackCradleSpec" + where + isGmDistDir dir = + liftIO $ not <$> doesFileExist (dir ".." "ghc-mod.cabal") + sandboxCradle :: IOish m => FilePath -> MaybeT m Cradle sandboxCradle wdir = do sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 7fe5a33..62f872c 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -69,9 +69,14 @@ withGhcModEnv' withCradle dir opts f = c <- gmoChan <$> gmoAsk gbracket_ (liftIO $ forkIO $ stdoutGateway c) (liftIO . killThread) a - withCradleRootDir (cradleRootDir -> projdir) = - gbracket_ (liftIO $ setCurrentDirectory projdir >> getCurrentDirectory) - (liftIO . setCurrentDirectory) + withCradleRootDir (cradleRootDir -> projdir) a = + gbracket_ (liftIO $ swapCurrentDirectory projdir) + (liftIO . setCurrentDirectory) a + + swapCurrentDirectory ndir = do + odir <- canonicalizePath =<< getCurrentDirectory + setCurrentDirectory ndir + return odir gbracket_ ma mb mc = gbracket ma mb (const mc) From 71c8361911ffefe8a095bab40bd66011922e4c0e Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 14 Sep 2015 13:07:51 +0900 Subject: [PATCH 125/147] making loop safer. --- elisp/ghc-process.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 47ceb24..63e74d3 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -122,7 +122,7 @@ (insert string) (goto-char (point-min)) (let ((cont t) end out) - (while (and cont (not (eobp))) + (while (and cont (not (eobp)) ghc-process-running) (cond ((looking-at "^O: ") (setq out t)) @@ -170,7 +170,8 @@ (setq ghc-process-running nil))))))) (defun ghc-process-sentinel (_process _event) - (setq ghc-process-running nil)) + (setq ghc-process-running nil) + (setq ghc-process-file-mapping nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; From 0277d4469536017be8bd179db6a4b6ab48a5b49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 07:32:49 +0200 Subject: [PATCH 126/147] Fix missing newline --- src/GHCMod.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index a1a8ea3..b2a086b 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -564,7 +564,7 @@ exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure exitError' :: Options -> String -> IO a exitError' opts msg = do - gmUnsafeErrStr (optOutput opts) msg + gmUnsafeErrStr (optOutput opts) $ dropWhileEnd (=='\n') msg ++ "\n" liftIO exitFailure fatalError :: String -> a From 983059101cc075f80ed6dc2f021f3186dfd3dced Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 10 Sep 2015 13:07:17 +0900 Subject: [PATCH 127/147] adding kill-buffer-hook. --- elisp/ghc.el | 1 + 1 file changed, 1 insertion(+) diff --git a/elisp/ghc.el b/elisp/ghc.el index d1d7e1d..33f4297 100644 --- a/elisp/ghc.el +++ b/elisp/ghc.el @@ -117,6 +117,7 @@ (define-key haskell-mode-map ghc-next-hole-key 'ghc-goto-next-hole) (ghc-comp-init) (setq ghc-initialized t) + (add-hook 'kill-buffer-hook 'ghc-kill-process) (defadvice save-buffer (after ghc-check-syntax-on-save activate) "Check syntax with GHC when a haskell-mode buffer is saved." (when (eq 'haskell-mode major-mode) (ghc-check-syntax)))) From 935c51eb1caef2a8045f76c7a860a08a7b3d55c5 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 10 Sep 2015 13:07:34 +0900 Subject: [PATCH 128/147] nicer messages. --- elisp/ghc-process.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 63e74d3..c41e11a 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -215,8 +215,8 @@ (let* ((name ghc-process-process-name) (cpro (if name (get-process name)))) (if (not cpro) - (message "No process") + (message "No ghc-mod process") (delete-process cpro) - (message "A process was killed")))) + (message "ghc-mod process was killed")))) (provide 'ghc-process) From 52016b621061b1b2808dc7cd460f2d4d62093f00 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 14 Sep 2015 15:02:34 +0900 Subject: [PATCH 129/147] using ghc-get-project-root only when it is necessary. --- elisp/ghc-process.el | 94 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index c41e11a..8ca481b 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -35,53 +35,53 @@ (ghc-run-ghc-mod '("root"))) (defun ghc-with-process (cmd callback &optional hook1 hook2) - (let ((root (ghc-get-project-root))) - (unless ghc-process-process-name - (setq ghc-process-process-name root)) - (when (and ghc-process-process-name (not ghc-process-running)) - (setq ghc-process-running t) - (if hook1 (funcall hook1)) - (let* ((cbuf (current-buffer)) - (name ghc-process-process-name) - (buf (get-buffer-create (concat " ghc-mod:" name))) - (file (buffer-file-name)) - (cpro (get-process name))) - (ghc-with-current-buffer buf - (setq ghc-process-original-buffer cbuf) - (setq ghc-process-original-file file) - (setq ghc-process-hook hook2) - (setq ghc-process-root root) - (let ((pro (ghc-get-process cpro name buf)) - (map-cmd (format "map-file %s\n" file))) - ;; map-file - (setq ghc-process-file-mapping t) - (setq ghc-process-callback nil) - (erase-buffer) - (when ghc-debug - (ghc-with-debug-buffer - (insert (format "%% %s" map-cmd)) - (insert "CONTENTS + EOT\n"))) - (process-send-string pro map-cmd) - (with-current-buffer cbuf - (save-restriction - (widen) - (process-send-region pro (point-min) (point-max)))) - (process-send-string pro "\004\n") - (condition-case nil - (let ((inhibit-quit nil)) - (while ghc-process-file-mapping - (accept-process-output pro 0.1 nil t))) - (quit - (setq ghc-process-running nil) - (setq ghc-process-file-mapping nil))) - ;; command - (setq ghc-process-callback callback) - (erase-buffer) - (when ghc-debug - (ghc-with-debug-buffer - (insert (format "%% %s" cmd)))) - (process-send-string pro cmd) - pro)))))) + (unless ghc-process-process-name + (setq ghc-process-process-name (ghc-get-project-root))) + (when (and ghc-process-process-name (not ghc-process-running)) + (setq ghc-process-running t) + (if hook1 (funcall hook1)) + (let* ((cbuf (current-buffer)) + (name ghc-process-process-name) + (root ghc-process-process-name) + (buf (get-buffer-create (concat " ghc-mod:" name))) + (file (buffer-file-name)) + (cpro (get-process name))) + (ghc-with-current-buffer buf + (setq ghc-process-original-buffer cbuf) + (setq ghc-process-original-file file) + (setq ghc-process-hook hook2) + (setq ghc-process-root root) + (let ((pro (ghc-get-process cpro name buf)) + (map-cmd (format "map-file %s\n" file))) + ;; map-file + (setq ghc-process-file-mapping t) + (setq ghc-process-callback nil) + (erase-buffer) + (when ghc-debug + (ghc-with-debug-buffer + (insert (format "%% %s" map-cmd)) + (insert "CONTENTS + EOT\n"))) + (process-send-string pro map-cmd) + (with-current-buffer cbuf + (save-restriction + (widen) + (process-send-region pro (point-min) (point-max)))) + (process-send-string pro "\004\n") + (condition-case nil + (let ((inhibit-quit nil)) + (while ghc-process-file-mapping + (accept-process-output pro 0.1 nil t))) + (quit + (setq ghc-process-running nil) + (setq ghc-process-file-mapping nil))) + ;; command + (setq ghc-process-callback callback) + (erase-buffer) + (when ghc-debug + (ghc-with-debug-buffer + (insert (format "%% %s" cmd)))) + (process-send-string pro cmd) + pro))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; From 6488f1070d531a15e0b71c5e1e4d44d092b5858d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Mon, 14 Sep 2015 15:09:34 +0900 Subject: [PATCH 130/147] boot skips map-file. --- elisp/ghc-comp.el | 2 +- elisp/ghc-process.el | 47 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/elisp/ghc-comp.el b/elisp/ghc-comp.el index 20be9a1..700e8e3 100644 --- a/elisp/ghc-comp.el +++ b/elisp/ghc-comp.el @@ -101,7 +101,7 @@ unloaded modules are loaded") (defun ghc-boot (n) (prog2 (message "Initializing...") - (ghc-sync-process "boot\n" n) + (ghc-sync-process "boot\n" n nil 'skip-map-file) (message "Initializing...done"))) (defun ghc-load-modules (mods) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 8ca481b..f861677 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -34,7 +34,7 @@ (defun ghc-get-project-root () (ghc-run-ghc-mod '("root"))) -(defun ghc-with-process (cmd callback &optional hook1 hook2) +(defun ghc-with-process (cmd callback &optional hook1 hook2 skip-map-file) (unless ghc-process-process-name (setq ghc-process-process-name (ghc-get-project-root))) (when (and ghc-process-process-name (not ghc-process-running)) @@ -54,26 +54,27 @@ (let ((pro (ghc-get-process cpro name buf)) (map-cmd (format "map-file %s\n" file))) ;; map-file - (setq ghc-process-file-mapping t) - (setq ghc-process-callback nil) - (erase-buffer) - (when ghc-debug - (ghc-with-debug-buffer - (insert (format "%% %s" map-cmd)) - (insert "CONTENTS + EOT\n"))) - (process-send-string pro map-cmd) - (with-current-buffer cbuf - (save-restriction - (widen) - (process-send-region pro (point-min) (point-max)))) - (process-send-string pro "\004\n") - (condition-case nil - (let ((inhibit-quit nil)) - (while ghc-process-file-mapping - (accept-process-output pro 0.1 nil t))) - (quit - (setq ghc-process-running nil) - (setq ghc-process-file-mapping nil))) + (unless skip-map-file + (setq ghc-process-file-mapping t) + (setq ghc-process-callback nil) + (erase-buffer) + (when ghc-debug + (ghc-with-debug-buffer + (insert (format "%% %s" map-cmd)) + (insert "CONTENTS + EOT\n"))) + (process-send-string pro map-cmd) + (with-current-buffer cbuf + (save-restriction + (widen) + (process-send-region pro (point-min) (point-max)))) + (process-send-string pro "\004\n") + (condition-case nil + (let ((inhibit-quit nil)) + (while ghc-process-file-mapping + (accept-process-output pro 0.1 nil t))) + (quit + (setq ghc-process-running nil) + (setq ghc-process-file-mapping nil)))) ;; command (setq ghc-process-callback callback) (erase-buffer) @@ -179,12 +180,12 @@ (defvar ghc-process-num-of-results nil) (defvar ghc-process-results nil) -(defun ghc-sync-process (cmd &optional n hook) +(defun ghc-sync-process (cmd &optional n hook skip-map-file) (unless ghc-process-running (setq ghc-process-rendezvous nil) (setq ghc-process-results nil) (setq ghc-process-num-of-results (or n 1)) - (let ((pro (ghc-with-process cmd 'ghc-process-callback nil hook))) + (let ((pro (ghc-with-process cmd 'ghc-process-callback nil hook skip-map-file))) ;; ghc-process-running is now t. ;; But if the process exits abnormally, it is set to nil. (condition-case nil From 56902bfe2d5ef7910577d54c9269d758fe770e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 09:42:45 +0200 Subject: [PATCH 131/147] Don't mess with cwd, causes too many race conditions I would just fork() but we have to support WinDOS, gah. --- Language/Haskell/GhcMod/Debug.hs | 6 +-- Language/Haskell/GhcMod/Error.hs | 7 +++- Language/Haskell/GhcMod/Monad.hs | 65 +++++++++++++++----------------- Language/Haskell/GhcMod/Types.hs | 3 ++ src/GHCMod.hs | 54 +++++++++++++++----------- src/Misc.hs | 9 +++-- 6 files changed, 81 insertions(+), 63 deletions(-) diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index bb7b7ac..d5accbf 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -8,7 +8,6 @@ import qualified Data.Set as Set import Data.Char import Data.List.Split import Text.PrettyPrint -import Language.Haskell.GhcMod.Convert import Language.Haskell.GhcMod.Monad import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Internal @@ -16,6 +15,7 @@ import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.Pretty import Language.Haskell.GhcMod.Utils import Language.Haskell.GhcMod.PathsAndFiles +import Language.Haskell.GhcMod.Cradle ---------------------------------------------------------------- @@ -138,5 +138,5 @@ mapDoc kd ad m = vcat $ ---------------------------------------------------------------- -- | Obtaining root information. -rootInfo :: IOish m => GhcModT m String -rootInfo = convert' =<< cradleRootDir <$> cradle +rootInfo :: (IOish m, GmOut m) => m String +rootInfo = (++"\n") . cradleRootDir <$> findCradle diff --git a/Language/Haskell/GhcMod/Error.hs b/Language/Haskell/GhcMod/Error.hs index 21f5892..11df046 100644 --- a/Language/Haskell/GhcMod/Error.hs +++ b/Language/Haskell/GhcMod/Error.hs @@ -33,7 +33,7 @@ module Language.Haskell.GhcMod.Error ( , module Control.Exception ) where -import Control.Arrow +import Control.Arrow hiding ((<+>)) import Control.Exception import Control.Monad.Error hiding (MonadIO, liftIO) import qualified Data.Set as Set @@ -143,6 +143,11 @@ gmeDoc e = case e of GMEStackBootrap msg -> (text $ "Boostrapping stack project failed") <+>: text msg + GMEWrongWorkingDirectory projdir cdir -> + (text $ "You must run ghc-mod in the project directory as returned by `ghc-mod root`.") + <+> text "Currently in:" <+> showDoc cdir + <> text "but should be in" <+> showDoc projdir + <> text "." ghcExceptionDoc :: GhcException -> Doc ghcExceptionDoc e@(CmdLineError _) = diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 62f872c..51b90a8 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -17,9 +17,9 @@ {-# LANGUAGE CPP #-} module Language.Haskell.GhcMod.Monad ( runGmOutT + , runGmOutT' , runGhcModT , runGhcModT' - , runGhcModT'' , hoistGhcModT , runGmlT , runGmlT' @@ -60,45 +60,42 @@ withGhcModEnv = withGhcModEnv' withCradle withGhcModEnv' :: (IOish m, GmOut m) => (FilePath -> (Cradle -> m a) -> m a) -> FilePath -> Options -> (GhcModEnv -> m a) -> m a withGhcModEnv' withCradle dir opts f = - withStdoutGateway $ - withCradle dir $ \crdl -> - withCradleRootDir crdl $ - f $ GhcModEnv opts crdl + withCradle dir $ \crdl -> + withCradleRootDir crdl $ + f $ GhcModEnv opts crdl where - withStdoutGateway a = do - c <- gmoChan <$> gmoAsk - gbracket_ (liftIO $ forkIO $ stdoutGateway c) (liftIO . killThread) a + withCradleRootDir (cradleRootDir -> projdir) a = do + cdir <- liftIO $ getCurrentDirectory + eq <- liftIO $ pathsEqual projdir cdir + if not eq + then throw $ GMEWrongWorkingDirectory projdir cdir + else a - withCradleRootDir (cradleRootDir -> projdir) a = - gbracket_ (liftIO $ swapCurrentDirectory projdir) - (liftIO . setCurrentDirectory) a + pathsEqual a b = do + ca <- canonicalizePath a + cb <- canonicalizePath b + return $ ca == cb - swapCurrentDirectory ndir = do - odir <- canonicalizePath =<< getCurrentDirectory - setCurrentDirectory ndir - return odir +runGmOutT :: IOish m => Options -> GmOutT m a -> m a +runGmOutT opts ma = do + gmo <- GhcModOut (optOutput opts) <$> liftIO newChan + runGmOutT' gmo ma - gbracket_ ma mb mc = gbracket ma mb (const mc) +runGmOutT' :: IOish m => GhcModOut -> GmOutT m a -> m a +runGmOutT' gmo ma = do + gbracket_ (liftIO $ forkIO $ stdoutGateway $ gmoChan gmo) + (liftIO . killThread) + (flip runReaderT gmo $ unGmOutT ma) -- | Run a @GhcModT m@ computation. -runGhcModT :: IOish m +runGhcModT :: (IOish m, GmOut m) => Options -> GhcModT m a -> m (Either GhcModError a, GhcModLog) -runGhcModT opt action = do - dir <- liftIO getCurrentDirectory - runGhcModT' dir opt action - -runGhcModT' :: IOish m - => FilePath - -> Options - -> GhcModT m a - -> m (Either GhcModError a, GhcModLog) -runGhcModT' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> do - gmo <- GhcModOut (optOutput opt) <$> liftIO newChan - runGmOutT gmo $ +runGhcModT opt action = liftIO (getCurrentDirectory >>= canonicalizePath) >>= \dir' -> do + runGmOutT opt $ withGhcModEnv dir' opt $ \env -> - first (fst <$>) <$> runGhcModT'' env defaultGhcModState + first (fst <$>) <$> runGhcModT' env defaultGhcModState (gmSetLogLevel (ooptLogLevel $ optOutput opt) >> action) -- | @hoistGhcModT result@. Embed a GhcModT computation's result into a GhcModT @@ -118,13 +115,13 @@ hoistGhcModT (r,l) = do -- do with 'GhcModEnv' and 'GhcModState'. -- -- You should probably look at 'runGhcModT' instead. -runGhcModT'' :: IOish m +runGhcModT' :: IOish m => GhcModEnv -> GhcModState -> GhcModT m a -> GmOutT m (Either GhcModError (a, GhcModState), GhcModLog) -runGhcModT'' r s a = do +runGhcModT' r s a = do flip runReaderT r $ runJournalT $ runErrorT $ runStateT (unGmT a) s -runGmOutT :: IOish m => GhcModOut -> GmOutT m a -> m a -runGmOutT gmo ma = flip runReaderT gmo $ unGmOutT ma +gbracket_ :: ExceptionMonad m => m a -> (a -> m b) -> m c -> m c +gbracket_ ma mb mc = gbracket ma mb (const mc) diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 473c56e..88cc6e5 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -403,6 +403,9 @@ data GhcModError | GMEStackBootrap String -- ^ Bootstrapping @stack@ environment failed (process exited with failure) + + | GMEWrongWorkingDirectory FilePath FilePath + deriving (Eq,Show,Typeable) instance Error GhcModError where diff --git a/src/GHCMod.hs b/src/GHCMod.hs index b2a086b..a8cb548 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -18,6 +18,7 @@ import Exception import Language.Haskell.GhcMod import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO) import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad import Paths_ghc_mod import System.Console.GetOpt (OptDescr(..), ArgDescr(..), ArgOrder(..)) import qualified System.Console.GetOpt as O @@ -402,24 +403,10 @@ main = do ] progMain :: (Options,[String]) -> IO () -progMain (globalOptions,cmdArgs) = hndle $ runGhcModT globalOptions $ handler $ do +progMain (globalOptions,cmdArgs) = runGmOutT globalOptions $ case globalCommands cmdArgs of Just s -> gmPutStr s - Nothing -> do - forM_ (reverse $ optFileMappings globalOptions) $ uncurry loadMMappedFiles - ghcCommands cmdArgs - where - hndle action = do - (e, _l) <- liftIO . evaluate =<< action - case e of - Right _ -> - return () - Left ed -> - exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc ed) - loadMMappedFiles from (Just to) = loadMappedFile from to - loadMMappedFiles from (Nothing) = do - src <- liftIO getFileSourceFromStdin - loadMappedFileSource from src + Nothing -> wrapGhcCommands globalOptions cmdArgs globalCommands :: [String] -> Maybe String globalCommands (cmd:_) @@ -433,7 +420,8 @@ legacyInteractive = do opt <- options prepareCabalHelper tmpdir <- cradleTempDir <$> cradle - symdbreq <- liftIO $ newSymDbReq opt tmpdir + gmo <- gmoAsk + symdbreq <- liftIO $ newSymDbReq opt gmo tmpdir world <- getCurrentWorld legacyInteractiveLoop symdbreq world @@ -523,6 +511,31 @@ getFileSourceFromStdin = do else loop' (acc++line++"\n") loop' "" +-- Someone please already rewrite the cmdline parsing code *weep* :'( +wrapGhcCommands :: (IOish m, GmOut m) => Options -> [String] -> m () +wrapGhcCommands _opts [] = fatalError "No command given (try --help)" +wrapGhcCommands _opts ("root":_) = gmPutStr =<< rootInfo +wrapGhcCommands opts args = do + handleGmError $ runGhcModT opts $ handler $ do + forM_ (reverse $ optFileMappings opts) $ + uncurry loadMMappedFiles + + ghcCommands args + where + handleGmError action = do + (e, _l) <- liftIO . evaluate =<< action + case e of + Right _ -> + return () + Left ed -> + exitError $ renderStyle ghcModStyle (gmeDoc ed) + + loadMMappedFiles from (Just to) = loadMappedFile from to + loadMMappedFiles from (Nothing) = do + src <- liftIO getFileSourceFromStdin + loadMappedFileSource from src + + ghcCommands :: IOish m => [String] -> GhcModT m () ghcCommands [] = fatalError "No command given (try --help)" ghcCommands (cmd:args) = gmPutStr =<< action args @@ -544,7 +557,7 @@ ghcCommands (cmd:args) = gmPutStr =<< action args "auto" -> autoCmd "find" -> findSymbolCmd "lint" -> lintCmd - "root" -> rootInfoCmd +-- "root" -> rootInfoCmd "doc" -> pkgDocCmd "dumpsym" -> dumpSymbolCmd "boot" -> bootCmd @@ -559,7 +572,7 @@ newtype InvalidCommandLine = InvalidCommandLine (Either String String) deriving (Show, Typeable) instance Exception InvalidCommandLine -exitError :: IOish m => String -> GhcModT m a +exitError :: (MonadIO m, GmOut m) => String -> m a exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure exitError' :: Options -> String -> IO a @@ -595,7 +608,7 @@ catchArgs cmd action = modulesCmd, languagesCmd, flagsCmd, browseCmd, checkSyntaxCmd, expandTemplateCmd, debugInfoCmd, componentInfoCmd, infoCmd, typesCmd, splitsCmd, sigCmd, - refineCmd, autoCmd, findSymbolCmd, lintCmd, rootInfoCmd, pkgDocCmd, + refineCmd, autoCmd, findSymbolCmd, lintCmd, pkgDocCmd, dumpSymbolCmd, bootCmd, legacyInteractiveCmd, nukeCachesCmd :: IOish m => [String] -> GhcModT m String @@ -604,7 +617,6 @@ modulesCmd = withParseCmd' "modules" s $ \[] -> modules languagesCmd = withParseCmd' "lang" [] $ \[] -> languages flagsCmd = withParseCmd' "flag" [] $ \[] -> flags debugInfoCmd = withParseCmd' "debug" [] $ \[] -> debugInfo -rootInfoCmd = withParseCmd' "root" [] $ \[] -> rootInfo componentInfoCmd = withParseCmd' "debugComponent" [] $ \ts -> componentInfo ts -- internal bootCmd = withParseCmd' "boot" [] $ \[] -> boot diff --git a/src/Misc.hs b/src/Misc.hs index bc5ff9d..a38c4ac 100644 --- a/src/Misc.hs +++ b/src/Misc.hs @@ -8,21 +8,22 @@ module Misc ( ) where import Control.Concurrent.Async (Async, async, wait) -import CoreMonad (liftIO) import Data.IORef (IORef, newIORef, readIORef, writeIORef) import Prelude import Language.Haskell.GhcMod import Language.Haskell.GhcMod.Internal hiding (MonadIO,liftIO) +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad ---------------------------------------------------------------- type SymDbReqAction = (Either GhcModError SymbolDb, GhcModLog) data SymDbReq = SymDbReq (IORef (Async SymDbReqAction)) (IO SymDbReqAction) -newSymDbReq :: Options -> FilePath -> IO SymDbReq -newSymDbReq opt dir = do - let act = runGhcModT opt $ loadSymbolDb dir +newSymDbReq :: Options -> GhcModOut -> FilePath -> IO SymDbReq +newSymDbReq opt gmo tmpdir = do + let act = runGmOutT' gmo $ runGhcModT opt $ loadSymbolDb tmpdir req <- async act ref <- newIORef req return $ SymDbReq ref act From 5b02cc1bb0a7859db7d97f009597c987063d73d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 09:44:16 +0200 Subject: [PATCH 132/147] Make sure stdoutGateway is flushed before exiting. --- Language/Haskell/GhcMod/Monad.hs | 8 ++-- Language/Haskell/GhcMod/Output.hs | 61 ++++++++++++++++++++++--------- Language/Haskell/GhcMod/Types.hs | 2 +- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index 51b90a8..b29f8b7 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -47,7 +47,7 @@ import Control.Monad.Reader (runReaderT) import Control.Monad.State.Strict (runStateT) import Control.Monad.Trans.Journal (runJournalT) -import Exception (ExceptionMonad(..)) +import Exception import System.Directory import Prelude @@ -82,9 +82,9 @@ runGmOutT opts ma = do runGmOutT' gmo ma runGmOutT' :: IOish m => GhcModOut -> GmOutT m a -> m a -runGmOutT' gmo ma = do - gbracket_ (liftIO $ forkIO $ stdoutGateway $ gmoChan gmo) - (liftIO . killThread) +runGmOutT' gmo@(gmoChan -> chan) ma = do + gbracket_ (liftIO $ forkIO $ stdoutGateway chan) + (const $ liftIO $ flushStdoutGateway chan) (flip runReaderT gmo $ unGmOutT ma) -- | Run a @GhcModT m@ computation. diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 8503861..21f7ea7 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -32,6 +32,7 @@ module Language.Haskell.GhcMod.Output ( , gmUnsafeErrStr , stdoutGateway + , flushStdoutGateway ) where import Data.List @@ -89,14 +90,14 @@ stdioOutputFns lpfx = let , liftIO . hPutStr stderr . unGmLine . errPfx) chanOutputFns :: MonadIO m - => Chan (GmStream, GmLines String) + => Chan (Either (MVar ()) (GmStream, GmLines String)) -> Maybe (String, String) -> (GmLines String -> m (), GmLines String -> m ()) chanOutputFns c lpfx = let (outPfx, errPfx) = pfxFns lpfx in - ( liftIO . writeChan c . (,) GmOutStream . outPfx - , liftIO . writeChan c . (,) GmErrStream . errPfx) + ( liftIO . writeChan c . Right . (,) GmOutStream . outPfx + , liftIO . writeChan c . Right . (,) GmErrStream . errPfx) outputFns' :: MonadIO m => GhcModOut -> (GmLines String -> m (), GmLines String -> m ()) @@ -142,23 +143,47 @@ gmReadProcess = do Nothing -> return $ readProcess -stdoutGateway :: Chan (GmStream, GmLines String) -> IO () +flushStdoutGateway :: Chan (Either (MVar ()) (GmStream, GmLines String)) -> IO () +flushStdoutGateway c = do + mv <- newEmptyMVar + writeChan c $ Left mv + takeMVar mv + +stdoutGateway :: Chan (Either (MVar ()) (GmStream, GmLines String)) -> IO () stdoutGateway chan = go ("", "") where - go buf@(obuf, ebuf) = do - (stream, GmLines ty l) <- readChan chan - case ty of - GmTerminated -> - case stream of - GmOutStream -> putStr (obuf++l) >> hFlush stdout >> go ("", ebuf) - GmErrStream -> putStr (ebuf++l) >> hFlush stdout >> go (obuf, "") - GmPartial -> case reverse $ lines l of - [] -> go buf - [x] -> go (appendBuf stream buf x) - x:xs -> do - putStr $ unlines $ reverse xs - hFlush stdout - go (appendBuf stream buf x) + go :: (String, String) -> IO () + go buf = do + cmd <- readChan chan + case cmd of + Left mv -> do + let flush (obuf, ebuf) = do + -- Add newline to unterminated stderr but not to stdout + -- otherwise emacs will get confused etc + putStr $ ebuf ++ if null ebuf || last ebuf /= '\n' + then "" else "\n" + putStr obuf + work (GmOutStream, GmLines GmPartial "") buf flush + putMVar mv () + Right l -> + work l buf go + + work (stream, GmLines ty l) buf@(obuf, ebuf) cont = case ty of + GmTerminated -> + case stream of + GmOutStream -> + putStr (obuf++l) >> hFlush stdout >> cont ("", ebuf) + GmErrStream -> + putStr (ebuf++l) >> hFlush stdout >> cont (obuf, "") + + GmPartial -> + case reverse $ lines l of + [] -> cont buf + [x] -> cont (appendBuf stream buf x) + x:xs -> do + putStr $ unlines $ reverse xs + hFlush stdout + cont (appendBuf stream buf x) appendBuf GmOutStream (obuf, ebuf) s = (obuf++s, ebuf) appendBuf GmErrStream (obuf, ebuf) s = (obuf, ebuf++s) diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 88cc6e5..15ca68a 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -192,7 +192,7 @@ data GhcModEnv = GhcModEnv { data GhcModOut = GhcModOut { gmoOptions :: OutputOpts - , gmoChan :: Chan (GmStream, GmLines String) + , gmoChan :: Chan (Either (MVar ()) (GmStream, GmLines String)) } data GhcModLog = GhcModLog { From 0aa3655e08508efeec71dd9d7594cbb3729f7a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Mon, 14 Sep 2015 10:11:33 +0200 Subject: [PATCH 133/147] Fix tests --- test/BrowseSpec.hs | 2 +- test/MonadSpec.hs | 2 +- test/TestUtils.hs | 29 ++++++++++++++++++----------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/test/BrowseSpec.hs b/test/BrowseSpec.hs index 09d7e6c..ebf8ed1 100644 --- a/test/BrowseSpec.hs +++ b/test/BrowseSpec.hs @@ -28,7 +28,7 @@ spec = do describe "`browse' in a project directory" $ do it "can list symbols defined in a a local module" $ do - withDirectory_ "test/data/ghc-mod-check/lib" $ do + withDirectory_ "test/data/ghc-mod-check/" $ do syms <- runD $ lines <$> browse "Data.Foo" syms `shouldContain` ["foo"] syms `shouldContain` ["fibonacci"] diff --git a/test/MonadSpec.hs b/test/MonadSpec.hs index 92cbdb3..5e60f55 100644 --- a/test/MonadSpec.hs +++ b/test/MonadSpec.hs @@ -9,7 +9,7 @@ spec = do describe "When using GhcModT in a do block" $ it "a pattern match failure causes a call to `fail` on ErrorT in the monad stack" $ do (a, _h) - <- runGhcModT defaultOptions $ + <- runGmOutDef $ runGhcModT defaultOptions $ do Just _ <- return Nothing return "hello" diff --git a/test/TestUtils.hs b/test/TestUtils.hs index 13d4de0..4514261 100644 --- a/test/TestUtils.hs +++ b/test/TestUtils.hs @@ -20,7 +20,6 @@ import Language.Haskell.GhcMod.Types import Control.Arrow import Control.Category -import Control.Concurrent import Control.Applicative import Control.Monad.Error (ErrorT, runErrorT) import Control.Monad.Trans.Journal @@ -45,11 +44,21 @@ extract action = do Left e -> error $ show e withSpecCradle :: (IOish m, GmOut m) => FilePath -> (Cradle -> m a) -> m a -withSpecCradle cradledir f = - gbracket (findSpecCradle cradledir) (liftIO . cleanupCradle) f +withSpecCradle cradledir f = do + gbracket (findSpecCradle cradledir) (liftIO . cleanupCradle) $ \crdl -> + bracketWorkingDirectory (cradleRootDir crdl) $ + f crdl -withGhcModEnvSpec :: (IOish m, GmOut m) => FilePath -> Options -> (GhcModEnv -> m a) -> m a -withGhcModEnvSpec = withGhcModEnv' withSpecCradle +bracketWorkingDirectory :: + (ExceptionMonad m, MonadIO m) => FilePath -> m c -> m c +bracketWorkingDirectory dir a = + gbracket (swapWorkingDirectory dir) (liftIO . setCurrentDirectory) (const a) + +swapWorkingDirectory :: MonadIO m => FilePath -> m FilePath +swapWorkingDirectory ndir = liftIO $ do + odir <- getCurrentDirectory >>= canonicalizePath + setCurrentDirectory $ ndir + return odir runGhcModTSpec :: Options -> GhcModT IO a -> IO (Either GhcModError a, GhcModLog) runGhcModTSpec opt action = do @@ -59,10 +68,9 @@ runGhcModTSpec opt action = do runGhcModTSpec' :: IOish m => FilePath -> Options -> GhcModT m b -> m (Either GhcModError b, GhcModLog) runGhcModTSpec' dir opt action = liftIO (canonicalizePath dir) >>= \dir' -> do - gmo <- GhcModOut (optOutput opt) <$> liftIO newChan - runGmOutT gmo $ - withGhcModEnvSpec dir' opt $ \env -> do - first (fst <$>) <$> runGhcModT'' env defaultGhcModState + runGmOutT opt $ + withGhcModEnv' withSpecCradle dir' opt $ \env -> do + first (fst <$>) <$> runGhcModT' env defaultGhcModState (gmSetLogLevel (ooptLogLevel $ optOutput opt) >> action) -- | Run GhcMod @@ -91,8 +99,7 @@ runNullLog action = do return a runGmOutDef :: IOish m => GmOutT m a -> m a -runGmOutDef = - runGmOutT (GhcModOut (optOutput defaultOptions) (error "no chan")) +runGmOutDef = runGmOutT defaultOptions shouldReturnError :: Show a => IO (Either GhcModError a, GhcModLog) From ad5a343d2e7c1a4468462c1220268d96a007510d Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Sep 2015 11:31:54 +0900 Subject: [PATCH 134/147] ensuring that ghc-mod is executed on the root dir. --- elisp/ghc-process.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index f861677..3322be1 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -87,13 +87,14 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ghc-get-process (cpro name buf) - (cond - ((not cpro) - (ghc-start-process name buf)) - ((not (eq (process-status cpro) 'run)) - (delete-process cpro) - (ghc-start-process name buf)) - (t cpro))) + (let ((default-directory name)) + (cond + ((not cpro) + (ghc-start-process name buf)) + ((not (eq (process-status cpro) 'run)) + (delete-process cpro) + (ghc-start-process name buf)) + (t cpro)))) (defun ghc-start-process (name buf) (let* ((process-connection-type nil) ;; using PIPE due to ^D From ac2d8ba134cd66e3807ec39590a7532a069c9112 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Sep 2015 11:32:16 +0900 Subject: [PATCH 135/147] kill buffer hook run only for haskell-mode. --- elisp/ghc-process.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 3322be1..db9f8f1 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -214,11 +214,12 @@ (defun ghc-kill-process () (interactive) - (let* ((name ghc-process-process-name) - (cpro (if name (get-process name)))) - (if (not cpro) - (message "No ghc-mod process") - (delete-process cpro) - (message "ghc-mod process was killed")))) + (when (eq major-mode 'haskell-mode) + (let* ((name ghc-process-process-name) + (cpro (if name (get-process name)))) + (if (not cpro) + (message "No ghc-mod process") + (delete-process cpro) + (message "ghc-mod process was killed"))))) (provide 'ghc-process) From 545f0557f2a2a4134da7ab7fa0b55e9fb91b65f4 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Sep 2015 12:14:36 +0900 Subject: [PATCH 136/147] ensuring that root ends with a file separator. --- elisp/ghc-func.el | 11 ++++++----- elisp/ghc-process.el | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/elisp/ghc-func.el b/elisp/ghc-func.el index 34eff59..886e552 100644 --- a/elisp/ghc-func.el +++ b/elisp/ghc-func.el @@ -207,12 +207,13 @@ (defun ghc-run-ghc-mod (cmds &optional prog) (let ((target (or prog ghc-module-command))) (ghc-executable-find target - (let ((cdir default-directory)) + (let ((cdir (or ghc-process-root ;; ghc-mod version/debug + default-directory))) ;; ghc-mod root (with-temp-buffer - (cd cdir) - (apply 'ghc-call-process target nil t nil - (append (ghc-make-ghc-options) cmds)) - (buffer-substring (point-min) (1- (point-max)))))))) + (let ((default-directory cdir)) + (apply 'ghc-call-process target nil t nil + (append (ghc-make-ghc-options) cmds)) + (buffer-substring (point-min) (1- (point-max))))))))) (defmacro ghc-executable-find (cmd &rest body) ;; (declare (indent 1)) diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index db9f8f1..75be11d 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -42,7 +42,7 @@ (if hook1 (funcall hook1)) (let* ((cbuf (current-buffer)) (name ghc-process-process-name) - (root ghc-process-process-name) + (root (file-name-as-directory ghc-process-process-name)) (buf (get-buffer-create (concat " ghc-mod:" name))) (file (buffer-file-name)) (cpro (get-process name))) @@ -51,7 +51,7 @@ (setq ghc-process-original-file file) (setq ghc-process-hook hook2) (setq ghc-process-root root) - (let ((pro (ghc-get-process cpro name buf)) + (let ((pro (ghc-get-process cpro name buf root)) (map-cmd (format "map-file %s\n" file))) ;; map-file (unless skip-map-file @@ -86,18 +86,18 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun ghc-get-process (cpro name buf) - (let ((default-directory name)) - (cond - ((not cpro) - (ghc-start-process name buf)) - ((not (eq (process-status cpro) 'run)) - (delete-process cpro) - (ghc-start-process name buf)) - (t cpro)))) +(defun ghc-get-process (cpro name buf root) + (cond + ((not cpro) + (ghc-start-process name buf root)) + ((not (eq (process-status cpro) 'run)) + (delete-process cpro) + (ghc-start-process name buf root)) + (t cpro))) -(defun ghc-start-process (name buf) - (let* ((process-connection-type nil) ;; using PIPE due to ^D +(defun ghc-start-process (name buf root) + (let* ((default-directory root) + (process-connection-type nil) ;; using PIPE due to ^D (opts (append ghc-debug-options '("-b" "\n" "-l" "--line-prefix=O: ,E: ") (ghc-make-ghc-options) From 0b2a3458fdba4b2fa60d58ab8124b38e77031408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 15 Sep 2015 05:25:00 +0200 Subject: [PATCH 137/147] Move `stack` code into seperate module --- Language/Haskell/GhcMod/CabalHelper.hs | 11 +-- Language/Haskell/GhcMod/Cradle.hs | 1 + Language/Haskell/GhcMod/Debug.hs | 2 +- Language/Haskell/GhcMod/GhcPkg.hs | 2 +- Language/Haskell/GhcMod/PathsAndFiles.hs | 40 ----------- Language/Haskell/GhcMod/Stack.hs | 89 ++++++++++++++++++++++++ ghc-mod.cabal | 1 + 7 files changed, 94 insertions(+), 52 deletions(-) create mode 100644 Language/Haskell/GhcMod/Stack.hs diff --git a/Language/Haskell/GhcMod/CabalHelper.hs b/Language/Haskell/GhcMod/CabalHelper.hs index f9db18b..b95258c 100644 --- a/Language/Haskell/GhcMod/CabalHelper.hs +++ b/Language/Haskell/GhcMod/CabalHelper.hs @@ -42,6 +42,7 @@ import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Logging import Language.Haskell.GhcMod.Output import Language.Haskell.GhcMod.CustomPackageDb +import Language.Haskell.GhcMod.Stack import System.FilePath import System.Process import System.Exit @@ -137,16 +138,6 @@ prepareCabalHelper = do when (isCabalHelperProject $ cradleProject crdl) $ withCabal $ liftIO $ prepare readProc projdir distdir -patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs -patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do - Just ghc <- getStackGhcPath senv - Just ghcPkg <- getStackGhcPkgPath senv - return $ progs { - ghcProgram = ghc - , ghcPkgProgram = ghcPkg - } -patchStackPrograms _crdl progs = return progs - withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a withCabal action = do crdl <- cradle diff --git a/Language/Haskell/GhcMod/Cradle.hs b/Language/Haskell/GhcMod/Cradle.hs index c294d11..51d2afa 100644 --- a/Language/Haskell/GhcMod/Cradle.hs +++ b/Language/Haskell/GhcMod/Cradle.hs @@ -14,6 +14,7 @@ import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Types import Language.Haskell.GhcMod.Utils +import Language.Haskell.GhcMod.Stack import Control.Applicative import Control.Monad diff --git a/Language/Haskell/GhcMod/Debug.hs b/Language/Haskell/GhcMod/Debug.hs index d5accbf..c1dcc02 100644 --- a/Language/Haskell/GhcMod/Debug.hs +++ b/Language/Haskell/GhcMod/Debug.hs @@ -14,8 +14,8 @@ import Language.Haskell.GhcMod.Internal import Language.Haskell.GhcMod.Target import Language.Haskell.GhcMod.Pretty import Language.Haskell.GhcMod.Utils -import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.Cradle +import Language.Haskell.GhcMod.Stack ---------------------------------------------------------------- diff --git a/Language/Haskell/GhcMod/GhcPkg.hs b/Language/Haskell/GhcMod/GhcPkg.hs index 9bff334..db2581d 100644 --- a/Language/Haskell/GhcMod/GhcPkg.hs +++ b/Language/Haskell/GhcMod/GhcPkg.hs @@ -23,6 +23,7 @@ import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.CabalHelper import Language.Haskell.GhcMod.PathsAndFiles import Language.Haskell.GhcMod.CustomPackageDb +import Language.Haskell.GhcMod.Stack ghcVersion :: Int ghcVersion = read cProjectVersionInt @@ -72,7 +73,6 @@ getGhcPkgProgram = do _ -> return $ ghcPkgProgram progs - getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb] getPackageDbStack = do crdl <- cradle diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 959e2b6..5a02c4b 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -86,46 +86,6 @@ findStackConfigFile dir = do Just (d, Just a) -> return $ Just $ d a Just (_, Nothing) -> error "findStackConfigFile" -getStackEnv :: (IOish m, GmOut m) => FilePath -> m (Maybe StackEnv) -getStackEnv projdir = U.withDirectory_ projdir $ runMaybeT $ do - env <- map (liToTup . splitOn ": ") . lines <$> readStack ["path"] - let look k = fromJust $ lookup k env - return StackEnv { - seDistDir = look "dist-dir" - , seBinPath = splitSearchPath $ look "bin-path" - , seSnapshotPkgDb = look "snapshot-pkg-db" - , seLocalPkgDb = look "local-pkg-db" - } - where - liToTup [k,v] = (k,v) - liToTup _ = error "getStackEnv" - -getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath) -getStackGhcPath = findExecutablesInStackBinPath "ghc" - -getStackGhcPkgPath :: IOish m => StackEnv -> m (Maybe FilePath) -getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" - -findExecutablesInStackBinPath :: IOish m => String -> StackEnv -> m (Maybe FilePath) -findExecutablesInStackBinPath exe StackEnv {..} = - liftIO $ listToMaybe <$> findExecutablesInDirectories' seBinPath exe - -findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] -findExecutablesInDirectories' path binary = - U.findFilesWith' isExecutable path (binary <.> exeExtension) - where isExecutable file = do - perms <- getPermissions file - return $ executable perms - - exeExtension = if isWindows then "exe" else "" - -readStack :: (IOish m, GmOut m) => [String] -> MaybeT m String -readStack args = do - stack <- MaybeT $ liftIO $ findExecutable "stack" - readProc <- lift gmReadProcess - liftIO $ flip E.catch (\(e :: IOError) -> throw $ GMEStackBootrap $ show e) $ do - evaluate =<< readProc stack args "" - -- | Get path to sandbox config file getSandboxDb :: Cradle -> IO (Maybe GhcPkgDb) getSandboxDb crdl = do diff --git a/Language/Haskell/GhcMod/Stack.hs b/Language/Haskell/GhcMod/Stack.hs new file mode 100644 index 0000000..567fdda --- /dev/null +++ b/Language/Haskell/GhcMod/Stack.hs @@ -0,0 +1,89 @@ +-- ghc-mod: Making Haskell development *more* fun +-- Copyright (C) 2015 Daniel Gröber +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +module Language.Haskell.GhcMod.Stack where + + +import Control.Applicative +import Control.Exception as E +import Control.Monad +import Control.Monad.Trans.Maybe +import Control.Monad.Trans.Class +import Data.List +import Data.List.Split +import Data.Maybe +import System.Directory +import System.FilePath +import System.Info.Extra +import Exception + +import Language.Haskell.GhcMod.Types +import Language.Haskell.GhcMod.Monad.Types +import Language.Haskell.GhcMod.Output +import qualified Language.Haskell.GhcMod.Utils as U +import Prelude + +patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs +patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do + Just ghc <- getStackGhcPath senv + Just ghcPkg <- getStackGhcPkgPath senv + return $ progs { + ghcProgram = ghc + , ghcPkgProgram = ghcPkg + } +patchStackPrograms _crdl progs = return progs + +getStackEnv :: (IOish m, GmOut m) => FilePath -> m (Maybe StackEnv) +getStackEnv projdir = U.withDirectory_ projdir $ runMaybeT $ do + env <- map (liToTup . splitOn ": ") . lines <$> readStack ["path"] + let look k = fromJust $ lookup k env + return StackEnv { + seDistDir = look "dist-dir" + , seBinPath = splitSearchPath $ look "bin-path" + , seSnapshotPkgDb = look "snapshot-pkg-db" + , seLocalPkgDb = look "local-pkg-db" + } + where + liToTup [k,v] = (k,v) + liToTup _ = error "getStackEnv" + +getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath) +getStackGhcPath = findExecutablesInStackBinPath "ghc" + +getStackGhcPkgPath :: IOish m => StackEnv -> m (Maybe FilePath) +getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" + +findExecutablesInStackBinPath :: IOish m => String -> StackEnv -> m (Maybe FilePath) +findExecutablesInStackBinPath exe StackEnv {..} = + liftIO $ listToMaybe <$> findExecutablesInDirectories' seBinPath exe + +findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] +findExecutablesInDirectories' path binary = + U.findFilesWith' isExecutable path (binary <.> exeExtension) + where isExecutable file = do + perms <- getPermissions file + return $ executable perms + + exeExtension = if isWindows then "exe" else "" + +readStack :: (IOish m, GmOut m) => [String] -> MaybeT m String +readStack args = do + stack <- MaybeT $ liftIO $ findExecutable "stack" + readProc <- lift gmReadProcess + lift $ flip gcatch (\(e :: IOError) -> exToErr e) $ do + liftIO $ evaluate =<< readProc stack args "" + where + exToErr = throw . GMEStackBootrap . show diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 2d62ac1..827a5f4 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -138,6 +138,7 @@ Library Language.Haskell.GhcMod.Pretty Language.Haskell.GhcMod.Read Language.Haskell.GhcMod.SrcUtils + Language.Haskell.GhcMod.Stack Language.Haskell.GhcMod.Target Language.Haskell.GhcMod.Types Language.Haskell.GhcMod.Utils From 5af2c939b3dffe2ea85f842b6e805c8ee78af0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Tue, 15 Sep 2015 05:25:29 +0200 Subject: [PATCH 138/147] Cleanup some dead code --- Language/Haskell/GhcMod/Error.hs | 51 ++++---------------------------- Language/Haskell/GhcMod/Stack.hs | 2 +- Language/Haskell/GhcMod/Types.hs | 23 +++----------- 3 files changed, 10 insertions(+), 66 deletions(-) diff --git a/Language/Haskell/GhcMod/Error.hs b/Language/Haskell/GhcMod/Error.hs index 11df046..e69cc34 100644 --- a/Language/Haskell/GhcMod/Error.hs +++ b/Language/Haskell/GhcMod/Error.hs @@ -17,7 +17,6 @@ {-# LANGUAGE ExistentialQuantification #-} module Language.Haskell.GhcMod.Error ( GhcModError(..) - , GMConfigStateFileError(..) , GmError , gmeDoc , ghcExceptionDoc @@ -53,37 +52,6 @@ import Language.Haskell.GhcMod.Pretty type GmError m = MonadError GhcModError m -gmCsfeDoc :: GMConfigStateFileError -> Doc -gmCsfeDoc GMConfigStateFileNoHeader = text $ - "Saved package config file header is missing. " - ++ "Try re-running the 'configure' command." - -gmCsfeDoc GMConfigStateFileBadHeader = text $ - "Saved package config file header is corrupt. " - ++ "Try re-running the 'configure' command." - -gmCsfeDoc GMConfigStateFileNoParse = text $ - "Saved package config file body is corrupt. " - ++ "Try re-running the 'configure' command." - -gmCsfeDoc GMConfigStateFileMissing = text $ - "Run the 'configure' command first." - --- gmCsfeDoc (ConfigStateFileBadVersion oldCabal oldCompiler _) = text $ --- "You need to re-run the 'configure' command. " --- ++ "The version of Cabal being used has changed (was " --- ++ display oldCabal ++ ", now " --- ++ display currentCabalId ++ ")." --- ++ badCompiler --- where --- badCompiler --- | oldCompiler == currentCompilerId = "" --- | otherwise = --- " Additionally the compiler is different (was " --- ++ display oldCompiler ++ ", now " --- ++ display currentCompilerId --- ++ ") which is probably the cause of the problem." - gmeDoc :: GhcModError -> Doc gmeDoc e = case e of GMENoMsg -> @@ -91,12 +59,11 @@ gmeDoc e = case e of GMEString msg -> text msg GMECabalConfigure msg -> - text "Configuring cabal project failed: " <> gmeDoc msg - GMECabalFlags msg -> - text "Retrieval of the cabal configuration flags failed: " <> gmeDoc msg - GMECabalComponent cn -> - text "Cabal component " <> quotes (gmComponentNameDoc cn) - <> text " could not be found." + text "Configuring cabal project failed" <+>: gmeDoc msg + GMEStackConfigure msg -> + text "Configuring stack project failed" <+>: gmeDoc msg + GMEStackBootstrap msg -> + text "Bootstrapping stack project environment failed" <+>: gmeDoc msg GMECabalCompAssignment ctx -> text "Could not find a consistent component assignment for modules:" $$ (nest 4 $ foldr ($+$) empty $ map ctxDoc ctx) $$ @@ -125,7 +92,6 @@ gmeDoc e = case e of compsDoc sc | Set.null sc = text "has no known components" compsDoc sc = fsep $ punctuate comma $ map gmComponentNameDoc $ Set.toList sc - GMEProcess _fn cmd args emsg -> let c = showCommandForUser cmd args in case emsg of Right err -> @@ -138,11 +104,6 @@ gmeDoc e = case e of GMETooManyCabalFiles cfs -> text $ "Multiple cabal files found. Possible cabal files: \"" ++ intercalate "\", \"" cfs ++"\"." - GMECabalStateFile csfe -> - gmCsfeDoc csfe - GMEStackBootrap msg -> - (text $ "Boostrapping stack project failed") - <+>: text msg GMEWrongWorkingDirectory projdir cdir -> (text $ "You must run ghc-mod in the project directory as returned by `ghc-mod root`.") <+> text "Currently in:" <+> showDoc cdir @@ -169,7 +130,6 @@ ghcExceptionDoc (Panic msg) = vcat $ map text $ lines $ printf "\ ghcExceptionDoc e = text $ showGhcException e "" - liftMaybe :: MonadError e m => e -> m (Maybe a) -> m a liftMaybe e action = maybe (throwError e) return =<< action @@ -183,7 +143,6 @@ infixr 0 `modifyError'` modifyError' :: MonadError e m => m a -> (e -> e) -> m a modifyError' = flip modifyError - modifyGmError :: (MonadIO m, ExceptionMonad m) => (GhcModError -> GhcModError) -> m a -> m a modifyGmError f a = gcatch a $ \(ex :: GhcModError) -> liftIO $ throwIO (f ex) diff --git a/Language/Haskell/GhcMod/Stack.hs b/Language/Haskell/GhcMod/Stack.hs index 567fdda..b4bdfc0 100644 --- a/Language/Haskell/GhcMod/Stack.hs +++ b/Language/Haskell/GhcMod/Stack.hs @@ -86,4 +86,4 @@ readStack args = do lift $ flip gcatch (\(e :: IOError) -> exToErr e) $ do liftIO $ evaluate =<< readProc stack args "" where - exToErr = throw . GMEStackBootrap . show + exToErr = throw . GMEStackBootstrap . GMEString . show diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index 15ca68a..f20f92f 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -379,11 +379,11 @@ data GhcModError | GMECabalConfigure GhcModError -- ^ Configuring a cabal project failed. - | GMECabalFlags GhcModError - -- ^ Retrieval of the cabal configuration flags failed. + | GMEStackConfigure GhcModError + -- ^ Configuring a stack project failed. - | GMECabalComponent ChComponentName - -- ^ Cabal component could not be found + | GMEStackBootstrap GhcModError + -- ^ Bootstrapping @stack@ environment failed (process exited with failure) | GMECabalCompAssignment [(Either FilePath ModuleName, Set ChComponentName)] -- ^ Could not find a consistent component assignment for modules @@ -398,12 +398,6 @@ data GhcModError | GMETooManyCabalFiles [FilePath] -- ^ Too many cabal files found. - | GMECabalStateFile GMConfigStateFileError - -- ^ Reading Cabal's state configuration file falied somehow. - - | GMEStackBootrap String - -- ^ Bootstrapping @stack@ environment failed (process exited with failure) - | GMEWrongWorkingDirectory FilePath FilePath deriving (Eq,Show,Typeable) @@ -414,15 +408,6 @@ instance Error GhcModError where instance Exception GhcModError -data GMConfigStateFileError - = GMConfigStateFileNoHeader - | GMConfigStateFileBadHeader - | GMConfigStateFileNoParse - | GMConfigStateFileMissing --- | GMConfigStateFileBadVersion PackageIdentifier PackageIdentifier (Either ConfigStateFileError LocalBuildInfo) - deriving (Eq, Show, Read, Typeable) - - deriving instance Generic Version instance Serialize Version From aad7cdebe541934ab671d7fd7fcdbac5098715be Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Tue, 15 Sep 2015 16:50:25 +0900 Subject: [PATCH 139/147] fixing doc (#599). --- elisp/ghc-doc.el | 18 ++++++++++-------- elisp/ghc-process.el | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/elisp/ghc-doc.el b/elisp/ghc-doc.el index 8142a83..fd782ef 100644 --- a/elisp/ghc-doc.el +++ b/elisp/ghc-doc.el @@ -41,14 +41,16 @@ (ghc-defstruct pkg-ver-path pkg ver path) (defun ghc-resolve-document-path (mod) - (with-temp-buffer - (ghc-call-process ghc-module-command nil t nil "doc" mod) - (goto-char (point-min)) - (when (looking-at "^\\([^ ]+\\)-\\([0-9]*\\(\\.[0-9]+\\)*\\) \\(.*\\)$") - (ghc-make-pkg-ver-path - :pkg (match-string-no-properties 1) - :ver (match-string-no-properties 2) - :path (match-string-no-properties 4))))) + (let ((root ghc-process-root)) + (with-temp-buffer + (let ((default-directory root)) + (ghc-call-process ghc-module-command nil t nil "doc" mod)) + (goto-char (point-min)) + (when (looking-at "^\\([^ ]+\\)-\\([0-9]*\\(\\.[0-9]+\\)*\\) \\(.*\\)$") + (ghc-make-pkg-ver-path + :pkg (match-string-no-properties 1) + :ver (match-string-no-properties 2) + :path (match-string-no-properties 4)))))) (defconst ghc-doc-local-format "file://%s/%s.html") (defconst ghc-doc-hackage-format diff --git a/elisp/ghc-process.el b/elisp/ghc-process.el index 75be11d..165d2f9 100644 --- a/elisp/ghc-process.el +++ b/elisp/ghc-process.el @@ -46,6 +46,8 @@ (buf (get-buffer-create (concat " ghc-mod:" name))) (file (buffer-file-name)) (cpro (get-process name))) + ;; setting root in the original buffer, sigh + (setq ghc-process-root root) (ghc-with-current-buffer buf (setq ghc-process-original-buffer cbuf) (setq ghc-process-original-file file) From 2c0d5af5e9d1f7ecd98c0150cacfe6133d1e0048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:10:12 +0200 Subject: [PATCH 140/147] Fix warnings --- Language/Haskell/GhcMod/PathsAndFiles.hs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Language/Haskell/GhcMod/PathsAndFiles.hs b/Language/Haskell/GhcMod/PathsAndFiles.hs index 5a02c4b..7f0aadf 100644 --- a/Language/Haskell/GhcMod/PathsAndFiles.hs +++ b/Language/Haskell/GhcMod/PathsAndFiles.hs @@ -24,10 +24,7 @@ import Control.Arrow (second) import Control.Applicative import Control.Exception as E import Control.Monad -import Control.Monad.Trans.Maybe -import Control.Monad.Trans.Class import Data.List -import Data.List.Split import Data.Char import Data.Maybe import Data.Traversable hiding (mapM) @@ -35,12 +32,9 @@ import Distribution.Helper (buildPlatform) import System.Directory import System.FilePath import System.Process -import System.Info.Extra import Language.Haskell.GhcMod.Types -import Language.Haskell.GhcMod.Monad.Types import Language.Haskell.GhcMod.Caching -import Language.Haskell.GhcMod.Output import qualified Language.Haskell.GhcMod.Utils as U import Utils (mightExist) import Prelude From 7e565df92386c1524ed834adf0012a1b0301dd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:08:16 +0200 Subject: [PATCH 141/147] Fix stdoutGateway line buffering --- Language/Haskell/GhcMod.hs | 2 - Language/Haskell/GhcMod/Output.hs | 188 ++++++++++++++---------------- Language/Haskell/GhcMod/Types.hs | 12 +- ghc-mod.cabal | 1 + src/GHCMod.hs | 7 +- 5 files changed, 89 insertions(+), 121 deletions(-) diff --git a/Language/Haskell/GhcMod.hs b/Language/Haskell/GhcMod.hs index 4cf0b38..46acf71 100644 --- a/Language/Haskell/GhcMod.hs +++ b/Language/Haskell/GhcMod.hs @@ -62,8 +62,6 @@ module Language.Haskell.GhcMod ( , gmErrStr , gmPutStrLn , gmErrStrLn - , gmUnsafePutStr - , gmUnsafeErrStr -- * FileMapping , loadMappedFile , loadMappedFileSource diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 21f7ea7..0d514a2 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -28,85 +28,56 @@ module Language.Haskell.GhcMod.Output ( , gmReadProcess - , gmUnsafePutStr - , gmUnsafeErrStr - , stdoutGateway , flushStdoutGateway ) where import Data.List +import qualified Data.Label as L +import qualified Data.Label.Base as LB import System.IO import System.Exit import System.Process import Control.Monad +import Control.Monad.State.Strict import Control.DeepSeq import Control.Exception -import Control.Concurrent +import Control.Concurrent (forkIO, killThread, myThreadId) +import Control.Concurrent.MVar +import Control.Concurrent.Chan +import Pipes +import Pipes.Lift import Prelude -import Language.Haskell.GhcMod.Types hiding (LineSeparator) -import Language.Haskell.GhcMod.Monad.Types - -withLines :: (String -> String) -> String -> String -withLines f s = let - res = unlines $ map f $ lines s - in - case s of - [] -> res - _ | not $ isTerminated s -> - reverse $ drop 1 $ reverse res - _ -> res - -isTerminated :: String -> Bool -isTerminated "" = False -isTerminated s = isNewline (last s) - -isNewline :: Char -> Bool -isNewline c = c == '\n' - -toGmLines :: String -> (GmLines String) -toGmLines "" = GmLines GmPartial "" -toGmLines s | isNewline (last s) = GmLines GmTerminated s -toGmLines s = GmLines GmPartial s +import Language.Haskell.GhcMod.Types hiding (LineSeparator, MonadIO(..)) +import Language.Haskell.GhcMod.Monad.Types hiding (MonadIO(..)) outputFns :: (GmOut m, MonadIO m') - => m (GmLines String -> m' (), GmLines String -> m' ()) + => m (String -> m' (), String -> m' ()) outputFns = outputFns' `liftM` gmoAsk -pfxFns :: Maybe (String, String) -> (GmLines String -> GmLines String, GmLines String -> GmLines String) -pfxFns lpfx = case lpfx of - Nothing -> ( id, id ) - Just (op, ep) -> ( fmap $ pfx (op++), fmap $ pfx (ep++) ) - where - pfx f = withLines f - -stdioOutputFns :: MonadIO m => Maybe (String, String) -> (GmLines String -> m (), GmLines String -> m ()) -stdioOutputFns lpfx = let - (outPfx, errPfx) = pfxFns lpfx - in - ( liftIO . putStr . unGmLine . outPfx - , liftIO . hPutStr stderr . unGmLine . errPfx) - -chanOutputFns :: MonadIO m - => Chan (Either (MVar ()) (GmStream, GmLines String)) - -> Maybe (String, String) - -> (GmLines String -> m (), GmLines String -> m ()) -chanOutputFns c lpfx = let - (outPfx, errPfx) = pfxFns lpfx - in - ( liftIO . writeChan c . Right . (,) GmOutStream . outPfx - , liftIO . writeChan c . Right . (,) GmErrStream . errPfx) - outputFns' :: - MonadIO m => GhcModOut -> (GmLines String -> m (), GmLines String -> m ()) + MonadIO m => GhcModOut -> (String -> m (), String -> m ()) outputFns' (GhcModOut oopts c) = let OutputOpts {..} = oopts in case ooptLinePrefix of - Nothing -> stdioOutputFns ooptLinePrefix - Just _ -> chanOutputFns c ooptLinePrefix + Nothing -> stdioOutputFns + Just _ -> chanOutputFns c + +stdioOutputFns :: MonadIO m => (String -> m (), String -> m ()) +stdioOutputFns = + ( liftIO . putStr + , liftIO . hPutStr stderr + ) + +chanOutputFns :: MonadIO m + => Chan (Either (MVar ()) (GmStream, String)) + -> (String -> m (), String -> m ()) +chanOutputFns c = (write GmOutStream, write GmErrStream) + where + write stream s = liftIO $ writeChan c $ Right $ (stream,s) gmPutStr, gmPutStrLn, gmErrStr, gmErrStrLn :: (MonadIO m, GmOut m) => String -> m () @@ -124,16 +95,10 @@ gmErrStrLn = gmErrStr . (++"\n") gmPutStrIO, gmErrStrIO :: (GmOut m, MonadIO mi) => m (String -> mi ()) -gmPutStrIO = ((. toGmLines) . fst) `liftM` outputFns -gmErrStrIO = ((. toGmLines) . snd) `liftM` outputFns +gmPutStrIO = fst `liftM` outputFns +gmErrStrIO = snd `liftM` outputFns --- | Only use these when you're sure there are no other writers on stdout -gmUnsafePutStr, gmUnsafeErrStr - :: MonadIO m => OutputOpts -> String -> m () -gmUnsafePutStr oopts = (fst $ stdioOutputFns (ooptLinePrefix oopts)) . toGmLines -gmUnsafeErrStr oopts = (snd $ stdioOutputFns (ooptLinePrefix oopts)) . toGmLines - gmReadProcess :: GmOut m => m (FilePath -> [String] -> String -> IO String) gmReadProcess = do GhcModOut {..} <- gmoAsk @@ -143,64 +108,83 @@ gmReadProcess = do Nothing -> return $ readProcess -flushStdoutGateway :: Chan (Either (MVar ()) (GmStream, GmLines String)) -> IO () +flushStdoutGateway :: Chan (Either (MVar ()) (GmStream, String)) -> IO () flushStdoutGateway c = do mv <- newEmptyMVar writeChan c $ Left mv takeMVar mv -stdoutGateway :: Chan (Either (MVar ()) (GmStream, GmLines String)) -> IO () -stdoutGateway chan = go ("", "") +type Line = String + +stdoutGateway :: (String, String) -> Chan (Either (MVar ()) (GmStream, String)) -> IO () +stdoutGateway (outPf, errPf) chan = do + runEffect $ commandProc >-> evalStateP ("","") seperateStreams where - go :: (String, String) -> IO () - go buf = do - cmd <- readChan chan + commandProc :: Producer (Either (MVar ()) (GmStream, String)) IO () + commandProc = do + cmd <- liftIO $ readChan chan case cmd of Left mv -> do - let flush (obuf, ebuf) = do - -- Add newline to unterminated stderr but not to stdout - -- otherwise emacs will get confused etc - putStr $ ebuf ++ if null ebuf || last ebuf /= '\n' - then "" else "\n" - putStr obuf - work (GmOutStream, GmLines GmPartial "") buf flush - putMVar mv () - Right l -> - work l buf go + yield $ Left mv + Right input -> do + yield $ Right input + commandProc - work (stream, GmLines ty l) buf@(obuf, ebuf) cont = case ty of - GmTerminated -> - case stream of - GmOutStream -> - putStr (obuf++l) >> hFlush stdout >> cont ("", ebuf) - GmErrStream -> - putStr (ebuf++l) >> hFlush stdout >> cont (obuf, "") + seperateStreams :: Consumer (Either (MVar ()) (GmStream, String)) (StateT (String, String) IO) () + seperateStreams = do + ecmd <- await + case ecmd of + Left mv -> do + -- flush buffers + (\s -> lift $ zoom (streamLens s) $ sGetLine Nothing) + `mapM_` [GmOutStream, GmErrStream] - GmPartial -> - case reverse $ lines l of - [] -> cont buf - [x] -> cont (appendBuf stream buf x) - x:xs -> do - putStr $ unlines $ reverse xs - hFlush stdout - cont (appendBuf stream buf x) + liftIO $ putMVar mv () + Right (stream, str) -> do + ls <- lift $ zoom (streamLens stream) $ sGetLine (Just str) + case ls of + [] -> return () + _ -> liftIO $ putStr $ unlines $ map (streamPf stream++) ls - appendBuf GmOutStream (obuf, ebuf) s = (obuf++s, ebuf) - appendBuf GmErrStream (obuf, ebuf) s = (obuf, ebuf++s) + liftIO $ hFlush stdout + seperateStreams + sGetLine :: (Maybe String) -> StateT String IO [Line] + sGetLine mstr' = do + buf <- get + let mstr = (buf++) <$> mstr' + case mstr of + Nothing -> put "" >> return [buf] + Just "" -> return [] + Just s | last s == '\n' -> put "" >> return (lines s) + | otherwise -> do + let (p:ls') = reverse $ lines s + put p + return $ reverse $ ls' + + streamLens GmOutStream = LB.fst + streamLens GmErrStream = LB.snd + + streamPf GmOutStream = outPf + streamPf GmErrStream = errPf + +zoom :: Monad m => (f L.:-> o) -> StateT o m a -> StateT f m a +zoom l (StateT a) = + StateT $ \f -> do + (a, s') <- a $ L.get l f + return (a, L.set l s' f) readProcessStderrChan :: GmOut m => m (FilePath -> [String] -> String -> IO String) readProcessStderrChan = do - (_, e :: GmLines String -> IO ()) <- outputFns + (_, e :: String -> IO ()) <- outputFns return $ readProcessStderrChan' e readProcessStderrChan' :: - (GmLines String -> IO ()) - -> FilePath -> [String] -> String -> IO String + (String -> IO ()) -> FilePath -> [String] -> String -> IO String readProcessStderrChan' pute = go pute where - go :: (GmLines String -> IO ()) -> FilePath -> [String] -> String -> IO String + go :: (String -> IO ()) -> FilePath -> [String] -> String -> IO String go putErr exe args input = do let cp = (proc exe args) { std_out = CreatePipe @@ -233,7 +217,7 @@ readProcessStderrChan' pute = go pute where ignoreSEx = handle (\(SomeException _) -> return ()) reader h = ignoreSEx $ do - putErr . toGmLines . (++"\n") =<< hGetLine h + putErr . (++"\n") =<< hGetLine h reader h withForkWait :: IO () -> (IO () -> IO a) -> IO a diff --git a/Language/Haskell/GhcMod/Types.hs b/Language/Haskell/GhcMod/Types.hs index f20f92f..ec1418d 100644 --- a/Language/Haskell/GhcMod/Types.hs +++ b/Language/Haskell/GhcMod/Types.hs @@ -172,19 +172,9 @@ data Cradle = Cradle { , cradleDistDir :: FilePath } deriving (Eq, Show) - data GmStream = GmOutStream | GmErrStream deriving (Show) -data GmLineType = GmTerminated | GmPartial - deriving (Show) - -data GmLines a = GmLines GmLineType a - deriving (Show, Functor) - -unGmLine :: GmLines a -> a -unGmLine (GmLines _ s) = s - data GhcModEnv = GhcModEnv { gmOptions :: Options , gmCradle :: Cradle @@ -192,7 +182,7 @@ data GhcModEnv = GhcModEnv { data GhcModOut = GhcModOut { gmoOptions :: OutputOpts - , gmoChan :: Chan (Either (MVar ()) (GmStream, GmLines String)) + , gmoChan :: Chan (Either (MVar ()) (GmStream, String)) } data GhcModLog = GhcModLog { diff --git a/ghc-mod.cabal b/ghc-mod.cabal index 827a5f4..bb1f14f 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -174,6 +174,7 @@ Library , djinn-ghc >= 0.0.2.2 , fclabels == 2.0.* , extra == 1.4.* + , pipes == 4.1.* if impl(ghc < 7.8) Build-Depends: convertible if impl(ghc < 7.5) diff --git a/src/GHCMod.hs b/src/GHCMod.hs index a8cb548..ea64153 100644 --- a/src/GHCMod.hs +++ b/src/GHCMod.hs @@ -399,7 +399,7 @@ main = do Left e -> throw e Right res@(globalOptions,_) -> catches (progMain res) [ Handler $ \(e :: GhcModError) -> - exitError' globalOptions $ renderStyle ghcModStyle (gmeDoc e) + runGmOutT globalOptions $ exitError $ renderStyle ghcModStyle (gmeDoc e) ] progMain :: (Options,[String]) -> IO () @@ -575,11 +575,6 @@ instance Exception InvalidCommandLine exitError :: (MonadIO m, GmOut m) => String -> m a exitError msg = gmErrStrLn (dropWhileEnd (=='\n') msg) >> liftIO exitFailure -exitError' :: Options -> String -> IO a -exitError' opts msg = do - gmUnsafeErrStr (optOutput opts) $ dropWhileEnd (=='\n') msg ++ "\n" - liftIO exitFailure - fatalError :: String -> a fatalError s = throw $ FatalError $ "ghc-mod: " ++ s From 413bac085db8b99dff6f0cc187473bc7c2999108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:09:35 +0200 Subject: [PATCH 142/147] Fix sharing stdout Chan with multiple threads --- Language/Haskell/GhcMod/Monad.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Language/Haskell/GhcMod/Monad.hs b/Language/Haskell/GhcMod/Monad.hs index b29f8b7..da79779 100644 --- a/Language/Haskell/GhcMod/Monad.hs +++ b/Language/Haskell/GhcMod/Monad.hs @@ -78,14 +78,17 @@ withGhcModEnv' withCradle dir opts f = runGmOutT :: IOish m => Options -> GmOutT m a -> m a runGmOutT opts ma = do - gmo <- GhcModOut (optOutput opts) <$> liftIO newChan - runGmOutT' gmo ma + gmo@GhcModOut{..} <- GhcModOut (optOutput opts) <$> liftIO newChan + let action = runGmOutT' gmo ma + case ooptLinePrefix $ optOutput opts of + Nothing -> action + Just pfxs -> + gbracket_ (liftIO $ forkIO $ stdoutGateway pfxs gmoChan) + (const $ liftIO $ flushStdoutGateway gmoChan) + action runGmOutT' :: IOish m => GhcModOut -> GmOutT m a -> m a -runGmOutT' gmo@(gmoChan -> chan) ma = do - gbracket_ (liftIO $ forkIO $ stdoutGateway chan) - (const $ liftIO $ flushStdoutGateway chan) - (flip runReaderT gmo $ unGmOutT ma) +runGmOutT' gmo ma = flip runReaderT gmo $ unGmOutT ma -- | Run a @GhcModT m@ computation. runGhcModT :: (IOish m, GmOut m) From 380acdaee0137619fb9be42af0ebb9b23dc296de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:09:55 +0200 Subject: [PATCH 143/147] Only use debugLogger when loglevel >= GmDebug --- Language/Haskell/GhcMod/DynFlags.hs | 4 ++++ Language/Haskell/GhcMod/Target.hs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/DynFlags.hs b/Language/Haskell/GhcMod/DynFlags.hs index 565c3ed..f335a7c 100644 --- a/Language/Haskell/GhcMod/DynFlags.hs +++ b/Language/Haskell/GhcMod/DynFlags.hs @@ -13,6 +13,10 @@ import Language.Haskell.GhcMod.DebugLogger import System.IO.Unsafe (unsafePerformIO) import Prelude +setEmptyLogger :: DynFlags -> DynFlags +setEmptyLogger df = + Gap.setLogAction df $ \_ _ _ _ _ -> return () + setDebugLogger :: (String -> IO ()) -> DynFlags -> DynFlags setDebugLogger put df = do Gap.setLogAction df (debugLogAction put) diff --git a/Language/Haskell/GhcMod/Target.hs b/Language/Haskell/GhcMod/Target.hs index 79ccaef..11decbe 100644 --- a/Language/Haskell/GhcMod/Target.hs +++ b/Language/Haskell/GhcMod/Target.hs @@ -137,9 +137,13 @@ runGmlTWith efnmns' mdf wrapper action = do (text "Initializing GHC session with following options") (intercalate " " $ map (("\""++) . (++"\"")) opts') + GhcModLog { gmLogLevel = Just level } <- gmlHistory putErr <- gmErrStrIO + let setLogger | level >= GmDebug = setDebugLogger putErr + | otherwise = setEmptyLogger + initSession opts' $ - setModeSimple >>> setDebugLogger putErr >>> mdf + setModeSimple >>> setLogger >>> mdf mappedStrs <- getMMappedFilePaths let targetStrs = mappedStrs ++ map moduleNameString mns ++ cfns @@ -441,6 +445,9 @@ loadTargets opts targetStrs = do loadTargets' Intelligent else loadTargets' Simple + + gmLog GmDebug "loadTargets" $ text "Loading done" + where relativize (Target (TargetFile filePath phase) taoc src) = do crdl <- cradle From 24050e5af3abca5f8cf6a12208825467229aeec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:18:44 +0200 Subject: [PATCH 144/147] Fix warnings --- Language/Haskell/GhcMod/Output.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 0d514a2..3dca02e 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -42,7 +42,7 @@ import Control.Monad import Control.Monad.State.Strict import Control.DeepSeq import Control.Exception -import Control.Concurrent (forkIO, killThread, myThreadId) +import Control.Concurrent (forkIO, killThread) import Control.Concurrent.MVar import Control.Concurrent.Chan import Pipes @@ -171,8 +171,8 @@ stdoutGateway (outPf, errPf) chan = do zoom :: Monad m => (f L.:-> o) -> StateT o m a -> StateT f m a zoom l (StateT a) = StateT $ \f -> do - (a, s') <- a $ L.get l f - return (a, L.set l s' f) + (a', s') <- a $ L.get l f + return (a', L.set l s' f) readProcessStderrChan :: GmOut m => m (FilePath -> [String] -> String -> IO String) From 46891f13ee2c079c433a3219ab05935f1a794e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:18:53 +0200 Subject: [PATCH 145/147] Fix #603, `stack path` output can have missing values --- Language/Haskell/GhcMod/Stack.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/Language/Haskell/GhcMod/Stack.hs b/Language/Haskell/GhcMod/Stack.hs index b4bdfc0..6cce20e 100644 --- a/Language/Haskell/GhcMod/Stack.hs +++ b/Language/Haskell/GhcMod/Stack.hs @@ -58,6 +58,7 @@ getStackEnv projdir = U.withDirectory_ projdir $ runMaybeT $ do } where liToTup [k,v] = (k,v) + liToTup [k] = (k, error "getStackEnv: missing key '"++k++"'") liToTup _ = error "getStackEnv" getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath) From 2c90e7a700d4c0ee4905647f0644b842813dac2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:30:07 +0200 Subject: [PATCH 146/147] Bump version to 5.4.0.0 --- elisp/ghc.el | 2 +- ghc-mod.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/elisp/ghc.el b/elisp/ghc.el index 33f4297..82c4fe5 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 "5.3.0.0") +(defconst ghc-version "5.4.0.0") (defgroup ghc-mod '() "ghc-mod customization") diff --git a/ghc-mod.cabal b/ghc-mod.cabal index bb1f14f..ac660bf 100644 --- a/ghc-mod.cabal +++ b/ghc-mod.cabal @@ -1,5 +1,5 @@ Name: ghc-mod -Version: 5.3.0.0 +Version: 5.4.0.0 Author: Kazu Yamamoto , Daniel Gröber , Alejandro Serrano From 0fde762500016d00ff3bed32d8dfca6e1b351b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Wed, 16 Sep 2015 05:40:53 +0200 Subject: [PATCH 147/147] Fix ghc<7.10 --- Language/Haskell/GhcMod/Output.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Language/Haskell/GhcMod/Output.hs b/Language/Haskell/GhcMod/Output.hs index 3dca02e..7b56330 100644 --- a/Language/Haskell/GhcMod/Output.hs +++ b/Language/Haskell/GhcMod/Output.hs @@ -152,7 +152,7 @@ stdoutGateway (outPf, errPf) chan = do sGetLine :: (Maybe String) -> StateT String IO [Line] sGetLine mstr' = do buf <- get - let mstr = (buf++) <$> mstr' + let mstr = (buf++) `liftM` mstr' case mstr of Nothing -> put "" >> return [buf] Just "" -> return []