Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 5f6f792

Browse files
committed
Merge pull request #130 from alanz/ghc-mod-session
Ghc mod session
2 parents 26efa02 + 3779426 commit 5f6f792

File tree

8 files changed

+88
-138
lines changed

8 files changed

+88
-138
lines changed

.travis.yml

+7-9
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,15 @@ install:
2727
# Bring in GHC if not already present
2828
- ./travis_long stack +RTS -N2 -RTS setup
2929
# Build the dependencies only.
30-
# - ./travis_long stack +RTS -N2 -RTS build --only-snapshot --pedantic
31-
- ./travis_long stack build --only-snapshot --pedantic
30+
# - ./travis_long stack build --only-snapshot --pedantic
31+
- ./travis_long stack build --only-snapshot
3232

3333
script:
34-
# - ./travis_long stack build --pedantic
35-
# - ./travis_long stack +RTS -N2 -RTS build
36-
- ./travis_long stack +RTS -N1 -RTS build --pedantic
37-
# - ./travis_long stack +RTS -N2 -RTS build --test --pedantic
38-
- ./travis_long stack +RTS -N1 -RTS build --test --pedantic
39-
# - ./travis_long stack build --test --pedantic
40-
# - ./travis_long stack +RTS -N2 -RTS build --test
34+
# Disabling pedantic due to warnings in current ghc-mod, which stack (erroneously) complains about
35+
# - ./travis_long stack +RTS -N1 -RTS build --pedantic
36+
- ./travis_long stack +RTS -N1 -RTS build
37+
# - ./travis_long stack +RTS -N1 -RTS build --test --pedantic
38+
- ./travis_long stack +RTS -N1 -RTS build --test
4139
- ./travis_long stack exec emacs24 -- -q --batch -L elisp -l elisp/tests/hie-tests.el -f ert-run-tests-batch-and-exit
4240
# Re-enable the haddock tests when the warning in HaRe does not trigger a problem
4341
# - ./travis_long stack +RTS -N2 -RTS build --test --haddock

Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ test-emacs:
1313

1414
.PHONY: test-haskell
1515
test-haskell:
16-
stack build --test --pedantic
16+
# stack build --test --pedantic
17+
# stack complains about deprecations in ghc-mod as an extra dep
18+
stack build --test
1719

1820
.PHONY: ghci-test
1921
ghci-test:

hie-ghc-mod/Haskell/Ide/GhcModPlugin.hs

+16-38
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE FlexibleContexts #-}
22
{-# LANGUAGE OverloadedStrings #-}
33
{-# LANGUAGE GADTs #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
45
module Haskell.Ide.GhcModPlugin where
56

67
import Control.Exception
@@ -14,8 +15,10 @@ import Haskell.Ide.Engine.PluginUtils
1415
import Haskell.Ide.Engine.SemanticTypes
1516
import qualified Language.Haskell.GhcMod as GM
1617
import qualified Language.Haskell.GhcMod.Monad as GM
18+
import qualified Language.Haskell.GhcMod.Types as GM
1719
import System.FilePath
1820
import System.Directory
21+
import qualified Exception as G
1922

2023
-- ---------------------------------------------------------------------
2124

@@ -74,7 +77,7 @@ checkCmd = CmdSync $ \_ctxs req -> do
7477
case getParams (IdFile "file" :& RNil) req of
7578
Left err -> return err
7679
Right (ParamFile fileName :& RNil) -> do
77-
fmap T.pack <$> liftIO (runGhcModCommand fileName (\f->GM.checkSyntax [f]))
80+
fmap T.pack <$> runGhcModCommand fileName (\f->GM.checkSyntax [f])
7881
Right _ -> return $ IdeResponseError (IdeError InternalError
7982
"GhcModPlugin.checkCmd: ghc’s exhaustiveness checker is broken" Nothing)
8083

@@ -102,7 +105,7 @@ lintCmd = CmdSync $ \_ctxs req -> do
102105
case getParams (IdFile "file" :& RNil) req of
103106
Left err -> return err
104107
Right (ParamFile fileName :& RNil) -> do
105-
fmap T.pack <$> liftIO (runGhcModCommand fileName GM.lint)
108+
fmap T.pack <$> runGhcModCommand fileName (GM.lint GM.defaultLintOpts)
106109
Right _ -> return $ IdeResponseError (IdeError InternalError
107110
"GhcModPlugin.lintCmd: ghc’s exhaustiveness checker is broken" Nothing)
108111

@@ -113,7 +116,7 @@ infoCmd = CmdSync $ \_ctxs req -> do
113116
case getParams (IdFile "file" :& IdText "expr" :& RNil) req of
114117
Left err -> return err
115118
Right (ParamFile fileName :& ParamText expr :& RNil) -> do
116-
fmap T.pack <$> liftIO (runGhcModCommand fileName (flip GM.info (GM.Expression (T.unpack expr))))
119+
fmap T.pack <$> runGhcModCommand fileName (flip GM.info (GM.Expression (T.unpack expr)))
117120
Right _ -> return $ IdeResponseError (IdeError InternalError
118121
"GhcModPlugin.infoCmd: ghc’s exhaustiveness checker is broken" Nothing)
119122

@@ -124,7 +127,7 @@ typeCmd = CmdSync $ \_ctxs req ->
124127
case getParams (IdFile "file" :& IdPos "start_pos" :& RNil) req of
125128
Left err -> return err
126129
Right (ParamFile fileName :& ParamPos (r,c) :& RNil) -> do
127-
fmap (toTypeInfo . T.lines . T.pack) <$> liftIO (runGhcModCommand fileName (\f->GM.types f r c))
130+
fmap (toTypeInfo . T.lines . T.pack) <$> runGhcModCommand fileName (\f->GM.types f r c)
128131
Right _ -> return $ IdeResponseError (IdeError InternalError
129132
"GhcModPlugin.typesCmd: ghc’s exhaustiveness checker is broken" Nothing)
130133

@@ -148,49 +151,24 @@ readTypeResult t = do
148151

149152
-- TODO: Need to thread the session through as in the commented out code below.
150153
runGhcModCommand :: T.Text -- ^ The file name we'll operate on
151-
-> (FilePath -> GM.GmT (GM.GmOutT (GM.GmOutT IO)) a) -> IO (IdeResponse a)
154+
-> (FilePath -> IdeM a)
155+
-> IdeM (IdeResponse a)
152156
runGhcModCommand fp cmd = do
153157
let (dir,f) = fileInfo fp
154158
let opts = GM.defaultOptions
155-
old <- getCurrentDirectory
156-
bracket (setCurrentDirectory dir)
157-
(\_ -> setCurrentDirectory old)
159+
old <- liftIO getCurrentDirectory
160+
G.gbracket (liftIO $ setCurrentDirectory dir)
161+
(\_ -> liftIO $ setCurrentDirectory old)
158162
(\_ -> do
159163
-- we need to get the root of our folder
160164
-- ghc-mod returns a new line at the end...
161165
root <- takeWhile (`notElem` ['\r','\n']) <$> GM.runGmOutT opts GM.rootInfo
162-
setCurrentDirectory root
163-
-- s <- GHC.getSession
164-
(r,_l) <- GM.runGmOutT opts $ GM.runGhcModT opts $ do
165-
-- (r,_l) <- GM.runGhcModT opts $ do
166-
-- GHC.setSession s
167-
-- s <- GM.getSession
168-
-- GM.setSession s
169-
-- setTargets [fileName]
170-
cr <- cmd f
171-
-- s' <- GHC.getSession
172-
let s' = undefined
173-
return (cr,s')
174-
-- (Either GM.GhcModError String, GM.GhcModLog)
175-
case r of
176-
Left e -> return $ IdeResponseError (IdeError PluginError (T.pack $ "doCheck:got " ++ show e) Nothing)
177-
Right (checkResult,_s3) -> do
178-
-- GHC.setSession s3
179-
return $ (IdeResponseOk checkResult)
166+
liftIO $ setCurrentDirectory root
167+
let setRoot e = e{GM.gmCradle = (GM.gmCradle e){GM.cradleRootDir=root}}
168+
(IdeResponseOk <$> GM.gmeLocal setRoot (cmd f)) `G.gcatch` \(e :: GM.GhcModError) ->
169+
return $ IdeResponseFail $ IdeError PluginError (T.pack $ "hie-ghc-mod: " ++ show e) Nothing
180170
)
181171

182-
{-
183-
dispatcher = runGmlT $ forever $ do
184-
s <- getSession
185-
(r, s') <- runGhcModT $ do
186-
setSession s
187-
r <- checkSyntax
188-
s <- getSession
189-
return (r,s)
190-
setSession s'
191-
192-
-}
193-
194172
-- ---------------------------------------------------------------------
195173

196174
-- | Returns the directory and file name
+26-30
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
11
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE FlexibleContexts #-}
23
{-# LANGUAGE MultiParamTypeClasses #-}
4+
{-# LANGUAGE LambdaCase #-}
35

46
module Haskell.Ide.Engine.Monad where
57

8+
import Control.Concurrent
9+
import Control.Concurrent.MVar
610
import Control.Exception
711
import Control.Monad.IO.Class
8-
import Control.Monad.State
9-
import Data.IORef
12+
import Control.Monad.State.Strict
13+
import Data.Char
1014
import Haskell.Ide.Engine.PluginDescriptor
1115
import qualified Language.Haskell.GhcMod.Monad as GM
1216
import qualified Language.Haskell.GhcMod.Types as GM
17+
import qualified Language.Haskell.GhcMod.Utils as GM
18+
import qualified Language.Haskell.GhcMod.Debug as GM
1319
import System.Directory
14-
20+
import System.IO.Unsafe
1521
-- ---------------------------------------------------------------------
1622

23+
runLock :: MVar ThreadId
24+
runLock = unsafePerformIO $ newEmptyMVar
25+
{-# NOINLINE runLock #-}
26+
1727
runIdeM :: IdeState -> IdeM a -> IO a
18-
runIdeM initState f = do
19-
initializedRef <- newIORef False
20-
let inner' = GM.runGmOutT opts $ GM.runGhcModT opts $ do
21-
liftIO $ writeIORef initializedRef True
22-
(unIdeM f)
23-
inner = runStateT inner' initState
24-
opts = GM.defaultOptions
25-
((eres, _),_s) <- inner `catch` \ex -> case ex of
26-
GM.GMEWrongWorkingDirectory projDir _ -> do
27-
-- Only switch dirs if the exception occurs during
28-
-- initialization. This way we don't mysteriously restart
29-
-- execution if the exception happens later.
30-
initialized <- readIORef initializedRef
31-
if initialized
32-
then throwIO ex
33-
else do
34-
old <- getCurrentDirectory
35-
bracket (setCurrentDirectory projDir)
36-
(\_ -> setCurrentDirectory old)
37-
(\_ -> inner)
38-
_ -> throwIO ex
39-
case eres of
40-
Left err -> throwIO err
41-
Right res -> return res
28+
runIdeM s0 f = do
29+
let errorIO e = liftIO $ throwIO $ ErrorCall e
4230

43-
-- ---------------------------------------------------------------------
31+
-- FIXME: this is very racy do some fancy stuff with masking
32+
-- _ <- liftIO $ (\case Just tid -> errorIO $ "locked by " ++ show tid)
33+
-- =<< tryReadMVar runLock
34+
-- liftIO $ putMVar runLock =<< myThreadId
4435

45-
setTargets :: [Either FilePath GM.ModuleName] -> IdeM ()
46-
setTargets targets = IdeM $ GM.runGmlT targets (return ())
36+
-- root <- either (error "could not get project root") (GM.dropWhileEnd isSpace) . fst
37+
-- <$> GM.runGhcModT GM.defaultOptions GM.rootInfo
4738

39+
-- liftIO $ setCurrentDirectory root
4840

41+
((eres, _),_s) <- flip runStateT s0 (GM.runGhcModT GM.defaultOptions f)
42+
case eres of
43+
Left err -> liftIO $ throwIO err
44+
Right res -> return res
4945

5046
-- EOF

hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs

+9-57
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,11 @@ module Haskell.Ide.Engine.PluginDescriptor
8383
, IdePlugins(..)
8484

8585
-- * The IDE monad
86-
, IdeM(..)
86+
, IdeM
8787
, IdeState(..)
88-
, HasIdeState(..)
88+
, getPlugins
8989
) where
9090

91-
import qualified DynFlags as GHC
92-
import qualified GHC as GHC
93-
import qualified HscTypes as GHC
94-
9591
import Control.Applicative
9692
import Data.Aeson
9793
import Data.Aeson.Types
@@ -100,10 +96,7 @@ import Data.Maybe
10096
import qualified Data.HashMap.Strict as H
10197
import qualified Data.Text as T
10298
import Data.Typeable
103-
import Control.Monad.IO.Class
104-
import Control.Monad.State
105-
import Control.Monad.Trans.Control ( control, liftBaseOp, liftBaseOp_)
106-
import Exception
99+
import Control.Monad.State.Strict
107100
import GHC.Generics
108101
import qualified Language.Haskell.GhcMod.Monad as GM
109102

@@ -131,7 +124,7 @@ instance Show PluginDescriptor where
131124
-- | Ideally a Command is defined in such a way that its CommandDescriptor
132125
-- can be exposed via the native CLI for the tool being exposed as well.
133126
-- Perhaps use Options.Applicative for this in some way.
134-
data Command = forall a .(ValidResponse a) => Command
127+
data Command = forall a. (ValidResponse a) => Command
135128
{ cmdDesc :: !CommandDescriptor
136129
, cmdFunc :: !(CommandFunc a)
137130
}
@@ -140,7 +133,7 @@ instance Show Command where
140133
show (Command desc _func) = "(Command " ++ show desc ++ ")"
141134

142135
-- | Build a command, ensuring the command response type name and the command function match
143-
buildCommand :: forall a .(ValidResponse a)
136+
buildCommand :: forall a. (ValidResponse a)
144137
=> CommandFunc a
145138
-> CommandName
146139
-> T.Text
@@ -597,56 +590,15 @@ instance (ValidResponse a) => FromJSON (IdeResponse a) where
597590

598591
-- ---------------------------------------------------------------------
599592

600-
newtype IdeM a = IdeM { unIdeM :: GM.GhcModT (GM.GmOutT (StateT IdeState IO)) a}
601-
deriving ( Functor
602-
, Applicative
603-
, Alternative
604-
, Monad
605-
, MonadPlus
606-
, MonadIO
607-
, GM.GmEnv
608-
, GM.GmOut
609-
, GM.MonadIO
610-
, ExceptionMonad
611-
)
593+
type IdeM = IdeT IO
594+
type IdeT m = GM.GhcModT (StateT IdeState m)
612595

613596
data IdeState = IdeState
614597
{
615598
idePlugins :: Plugins
616599
} deriving (Show)
617600

618-
class (Monad m) => HasIdeState m where
619-
getPlugins :: m Plugins
620-
-- | Set up an underlying GHC session for the specific targets. Should map
621-
-- down to ghc-mod setTargets.
622-
setTargets :: [FilePath] -> m ()
623-
624-
-- ---------------------------------------------------------------------
625-
626-
instance GM.MonadIO (StateT IdeState IO) where
627-
liftIO = liftIO
628-
629-
instance MonadState IdeState IdeM where
630-
get = IdeM (lift $ lift $ lift get)
631-
put s = IdeM (lift $ lift $ lift (put s))
632-
633-
instance GHC.GhcMonad IdeM where
634-
getSession = IdeM $ GM.unGmlT GM.gmlGetSession
635-
setSession env = IdeM $ GM.unGmlT (GM.gmlSetSession env)
636-
637-
instance GHC.HasDynFlags IdeM where
638-
getDynFlags = GHC.hsc_dflags <$> GHC.getSession
639-
640-
instance ExceptionMonad (StateT IdeState IO) where
641-
gcatch act handler = control $ \run ->
642-
run act `gcatch` (run . handler)
643-
644-
gmask = liftBaseOp gmask . liftRestore
645-
where liftRestore f r = f $ liftBaseOp_ r
646-
647-
-- AZ:TODO: These can become just MonadFunctions
648-
instance HasIdeState IdeM where
649-
getPlugins = gets idePlugins
650-
setTargets targets = IdeM $ GM.runGmlT (map Left targets) (return ())
601+
getPlugins :: IdeM Plugins
602+
getPlugins = lift $ lift $ idePlugins <$> get
651603

652604
-- EOF

hie-plugin-api/hie-plugin-api.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ library
2424
, directory
2525
, fast-logger
2626
, ghc
27-
, ghc-mod >= 5.4
27+
-- ghc-mod > 5.4.0.0 -- TODO before release
28+
, ghc-mod >= 5.4.0.0
2829
, lifted-base
2930
, monad-control
3031
, monad-logger

stack.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ packages:
77
- hie-plugin-api
88
- hie-ghc-mod
99
- hie-hare
10+
# - ../ghci-ng
11+
- location:
12+
git: https://github.com/kazu-yamamoto/ghc-mod.git
13+
commit: b9bd4ebf77b22d2d9061d647d7799ddcc7c51228
14+
# commit: bff86be69f556f80a8dcd9dd42774ab77cb00eba
15+
extra-dep: true
1016
extra-deps:
1117
- HaRe-0.8.2.1
1218
- rosezipper-0.2

0 commit comments

Comments
 (0)