diff --git a/docs/Architecture.md b/docs/Architecture.md index e5d0572a5..5ca31646e 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -40,7 +40,7 @@ getCachedModule :: Uri -> IdeM (CachedModuleResult) -- along with the cache or error if present data CachedModuleResult = ModuleLoading -- ^ The module has no cache yet and has not failed - | ModuleFailed T.Text + | ModuleFailed -- ^ The module has no cache but something went wrong | ModuleCached CachedModule IsStale -- ^ A cache exists for the module diff --git a/hie-plugin-api/Haskell/Ide/Engine/GhcModuleCache.hs b/hie-plugin-api/Haskell/Ide/Engine/GhcModuleCache.hs index d40fd318f..cbeeaa275 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/GhcModuleCache.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/GhcModuleCache.hs @@ -4,7 +4,6 @@ module Haskell.Ide.Engine.GhcModuleCache where -import qualified Data.Text as T import qualified Data.Map as Map import Data.Dynamic (Dynamic) import Data.Typeable (TypeRep) @@ -23,7 +22,7 @@ data UriCache = UriCache { cachedModule :: !CachedModule , cachedData :: !(Map.Map TypeRep Dynamic) , isStale :: !Bool - } | UriCacheFailed T.Text deriving Show + } | UriCacheFailed deriving Show data CachedModule = CachedModule { tcMod :: !TypecheckedModule diff --git a/hie-plugin-api/Haskell/Ide/Engine/ModuleCache.hs b/hie-plugin-api/Haskell/Ide/Engine/ModuleCache.hs index 724184a9d..3ddcd5d8d 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/ModuleCache.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/ModuleCache.hs @@ -6,12 +6,11 @@ module Haskell.Ide.Engine.ModuleCache where import Control.Monad import Control.Monad.IO.Class import Control.Monad.Trans.Control -import qualified Data.Aeson as J +import Control.Monad.Trans.Free import Data.Dynamic (toDyn, fromDynamic) import Data.Generics (Proxy(..), typeRep, typeOf) import qualified Data.Map as Map import Data.Maybe -import qualified Data.Text as T import Data.Typeable (Typeable) import Exception (ExceptionMonad) import System.Directory @@ -80,22 +79,22 @@ getCradle fp = do -- along with the cache or error if present data CachedModuleResult = ModuleLoading -- ^ The module has no cache yet and has not failed - | ModuleFailed T.Text + | ModuleFailed -- ^ The module has no cache because something went wrong | ModuleCached CachedModule IsStale -- ^ A cache exists for the module type IsStale = Bool -- | looks up a CachedModule for a given URI -getCachedModule :: (GM.MonadIO m, HasGhcModuleCache m) +getCachedModule :: (MonadIO m, HasGhcModuleCache m) => FilePath -> m CachedModuleResult getCachedModule uri = do uri' <- liftIO $ canonicalizePath uri maybeUriCache <- fmap (Map.lookup uri' . uriCaches) getModuleCache return $ case maybeUriCache of Nothing -> ModuleLoading - Just uriCache@(UriCache {}) -> ModuleCached (cachedModule uriCache) (isStale uriCache) - Just (UriCacheFailed err) -> ModuleFailed err + Just uriCache@UriCache {} -> ModuleCached (cachedModule uriCache) (isStale uriCache) + Just UriCacheFailed -> ModuleFailed -- | Returns true if there is a CachedModule for a given URI isCached :: (GM.MonadIO m, HasGhcModuleCache m) @@ -108,22 +107,10 @@ isCached uri = do -- | Version of `withCachedModuleAndData` that doesn't provide -- any extra cached data. -withCachedModule :: FilePath -> (CachedModule -> IdeM (IdeResponse b)) -> IdeM (IdeResponse b) -withCachedModule uri callback = withCachedModuleDefault uri Nothing callback - --- | Version of `withCachedModuleAndData` that doesn't provide --- any extra cached data. -withCachedModuleDefault :: FilePath -> Maybe (IdeResponse b) - -> (CachedModule -> IdeM (IdeResponse b)) -> IdeM (IdeResponse b) -withCachedModuleDefault uri mdef callback = do - mcm <- getCachedModule uri - uri' <- liftIO $ canonicalizePath uri - case mcm of - ModuleCached cm _ -> callback cm - ModuleLoading -> return $ IdeResponseDeferred uri' callback - ModuleFailed err -> case mdef of - Nothing -> return $ IdeResponseFail (IdeError NoModuleAvailable err J.Null) - Just def -> return def +withCachedModule :: FilePath -> a -> (CachedModule -> IdeM a) -> IdeM a +withCachedModule fp def callback = wrap (IdeDefer fp go) + where go UriCache{cachedModule = cm} = callback cm + go _ = return def -- | Calls its argument with the CachedModule for a given URI -- along with any data that might be stored in the ModuleCache. @@ -134,36 +121,26 @@ withCachedModuleDefault uri mdef callback = do -- If the data doesn't exist in the cache, new data is generated -- using by calling the `cacheDataProducer` function. withCachedModuleAndData :: forall a b. ModuleCache a - => FilePath -> (CachedModule -> a -> IdeM (IdeResponse b)) -> IdeM (IdeResponse b) -withCachedModuleAndData uri callback = withCachedModuleAndDataDefault uri Nothing callback - -withCachedModuleAndDataDefault :: forall a b. ModuleCache a - => FilePath -> Maybe (IdeResponse b) - -> (CachedModule -> a -> IdeM (IdeResponse b)) -> IdeM (IdeResponse b) -withCachedModuleAndDataDefault uri mdef callback = do - uri' <- liftIO $ canonicalizePath uri - mcache <- getModuleCache - let mc = (Map.lookup uri' . uriCaches) mcache - case mc of - Nothing -> return $ IdeResponseDeferred uri' $ \_ -> withCachedModuleAndData uri callback - Just (UriCacheFailed err) -> case mdef of - Nothing -> return $ IdeResponseFail (IdeError NoModuleAvailable err J.Null) - Just def -> return def - Just UriCache{cachedModule = cm, cachedData = dat} -> do - let proxy :: Proxy a - proxy = Proxy - a <- case Map.lookup (typeRep proxy) dat of - Nothing -> do - val <- cacheDataProducer cm - let dat' = Map.insert (typeOf val) (toDyn val) dat - modifyCache (\s -> s {uriCaches = Map.insert uri' (UriCache cm dat' False) - (uriCaches s)}) - return val - Just x -> - case fromDynamic x of - Just val -> return val - Nothing -> error "impossible" - callback cm a + => FilePath -> b + -> (CachedModule -> a -> IdeM b) -> IdeM b +withCachedModuleAndData fp def callback = wrap (IdeDefer fp go) + where go UriCacheFailed = return def + go UriCache{cachedModule = cm, cachedData = dat} = do + fp' <- liftIO $ canonicalizePath fp + let proxy :: Proxy a + proxy = Proxy + a <- case Map.lookup (typeRep proxy) dat of + Nothing -> do + val <- cacheDataProducer cm + let dat' = Map.insert (typeOf val) (toDyn val) dat + modifyCache (\s -> s {uriCaches = Map.insert fp' (UriCache cm dat' False) + (uriCaches s)}) + return val + Just x -> + case fromDynamic x of + Just val -> return val + Nothing -> error "impossible" + callback cm a -- | Saves a module to the cache and executes any deferred -- responses waiting on that module. @@ -171,40 +148,39 @@ cacheModule :: FilePath -> CachedModule -> IdeGhcM () cacheModule uri cm = do uri' <- liftIO $ canonicalizePath uri - modifyCache (\gmc -> - gmc { uriCaches = Map.insert - uri' - (UriCache cm Map.empty False) - (uriCaches gmc) - } - ) + let uc = UriCache cm Map.empty False + + modifyCache $ \gmc -> + gmc { uriCaches = Map.insert uri' uc (uriCaches gmc) } -- execute any queued actions for the module - runDeferredActions uri' (Right cm) + runDeferredActions uri' uc -- | Marks a module that it failed to load and triggers -- any deferred responses waiting on it -failModule :: FilePath -> T.Text -> IdeGhcM () -failModule fp err = do +failModule :: FilePath -> IdeGhcM () +failModule fp = do fp' <- liftIO $ canonicalizePath fp maybeUriCache <- fmap (Map.lookup fp' . uriCaches) getModuleCache + let uc = UriCacheFailed + case maybeUriCache of Just _ -> return () - Nothing -> do + Nothing -> -- If there's no cache for the module mark it as failed modifyCache (\gmc -> gmc { - uriCaches = Map.insert fp' (UriCacheFailed err) (uriCaches gmc) + uriCaches = Map.insert fp' uc (uriCaches gmc) } ) -- Fail the queued actions - runDeferredActions fp' (Left err) + runDeferredActions fp' uc -runDeferredActions :: FilePath -> Either T.Text CachedModule -> IdeGhcM () +runDeferredActions :: FilePath -> UriCache -> IdeGhcM () runDeferredActions uri cached = do actions <- fmap (fromMaybe [] . Map.lookup uri) (requestQueue <$> readMTS) liftToGhc $ forM_ actions (\a -> a cached) diff --git a/hie-plugin-api/Haskell/Ide/Engine/Monad.hs b/hie-plugin-api/Haskell/Ide/Engine/Monad.hs index 1d1a7b764..e67fe7367 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/Monad.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/Monad.hs @@ -2,7 +2,7 @@ module Haskell.Ide.Engine.Monad where import Control.Exception import Control.Monad.IO.Class -import Control.Monad.Reader +import Control.Monad.Reader import qualified GhcMod.Monad as GM import qualified GhcMod.Types as GM import Haskell.Ide.Engine.MonadTypes diff --git a/hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs b/hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs index 7bd1a5534..37394fdad 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/PluginDescriptor.hs @@ -43,7 +43,7 @@ toDynJSON = CD.toDyn -- arguments in the form of a JSON object. runPluginCommand :: PluginId -> CommandName -> Value -> IdeGhcM (IdeResult DynamicJSON) runPluginCommand p com arg = do - (IdePlugins m) <- lift . lift $ getPlugins + (IdePlugins m) <- lift $ lift $ lift getPlugins case Map.lookup p m of Nothing -> return $ IdeResultFail $ IdeError UnknownPlugin ("Plugin " <> p <> " doesn't exist") Null diff --git a/hie-plugin-api/Haskell/Ide/Engine/PluginUtils.hs b/hie-plugin-api/Haskell/Ide/Engine/PluginUtils.hs index ae9dff510..2625cae2c 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/PluginUtils.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/PluginUtils.hs @@ -8,7 +8,6 @@ module Haskell.Ide.Engine.PluginUtils ( mapEithers , pluginGetFile - , pluginGetFileResponse , makeDiffResult , WithDeletions(..) , makeAdditiveDiffResult @@ -122,16 +121,6 @@ pluginGetFile name uri f = Nothing -> return $ IdeResultFail (IdeError PluginError (name <> "Couldn't resolve uri" <> getUri uri) Null) --- | @pluginGetFile but for IdeResponse - use with IdeM -pluginGetFileResponse - :: Monad m - => T.Text -> Uri -> (FilePath -> m (IdeResponse a)) -> m (IdeResponse a) -pluginGetFileResponse name uri f = - case uriToFilePath uri of - Just file -> f file - Nothing -> return $ IdeResponseFail (IdeError PluginError - (name <> "Couldn't resolve uri" <> getUri uri) Null) - -- --------------------------------------------------------------------- -- courtesy of http://stackoverflow.com/questions/19891061/mapeithers-function-in-haskell mapEithers :: (a -> Either b c) -> [a] -> Either b [c] diff --git a/hie-plugin-api/Haskell/Ide/Engine/PluginsIdeMonads.hs b/hie-plugin-api/Haskell/Ide/Engine/PluginsIdeMonads.hs index 20e1f4855..60523517f 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/PluginsIdeMonads.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/PluginsIdeMonads.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} @@ -31,18 +32,15 @@ module Haskell.Ide.Engine.PluginsIdeMonads , SymbolProvider , IdePlugins(..) -- * The IDE monad - , IdeGhcM , IdeState(..) + , IdeGhcM , IdeM + , iterT , LiftsToGhc(..) - -- * IdeResult and IdeResponse + -- * IdeResult , IdeResult(..) , IdeResultT(..) - , pattern IdeResponseOk - , pattern IdeResponseFail - , IdeResponse - , IdeResponse'(..) - , IdeResponseT(..) + , IdeDefer(..) , IdeError(..) , IdeErrorCode(..) -- * LSP types @@ -64,6 +62,7 @@ module Haskell.Ide.Engine.PluginsIdeMonads import Control.Concurrent.STM import Control.Monad.IO.Class import Control.Monad.Reader +import Control.Monad.Trans.Free import Data.Aeson import Data.Dynamic (Dynamic) @@ -102,6 +101,7 @@ import Language.Haskell.LSP.Types (Command (..), WorkspaceEdit (..), filePathToUri, uriToFilePath) +import System.Directory type PluginId = T.Text @@ -116,7 +116,7 @@ data PluginCommand = forall a b. (FromJSON a, ToJSON b, Typeable b) => } -- --------------------------------------------------------------------- - + class Monad m => HasPidCache m where getPidCache :: m Int @@ -126,7 +126,7 @@ instance HasPidCache IdeM where instance HasPidCache IO where getPidCache = getProcessID -instance HasPidCache m => HasPidCache (IdeResponseT m) where +instance HasPidCache m => HasPidCache (IdeResultT m) where getPidCache = lift getPidCache mkLspCommand :: HasPidCache m => PluginId -> CommandName -> T.Text -> Maybe [Value] -> m Command @@ -152,7 +152,7 @@ type CodeActionProvider = PluginId -> Maybe FilePath -- ^ Project root directory -> Range -> CodeActionContext - -> IdeM (IdeResponse [CodeAction]) + -> IdeM (IdeResult [CodeAction]) -- type DiagnosticProviderFunc = DiagnosticTrigger -> Uri -> IdeM (IdeResponse (Map.Map Uri (S.Set Diagnostic))) type DiagnosticProviderFunc @@ -168,9 +168,9 @@ data DiagnosticTrigger = DiagnosticOnOpen | DiagnosticOnSave deriving (Show,Ord,Eq) -type HoverProvider = Uri -> Position -> IdeM (IdeResponse [Hover]) +type HoverProvider = Uri -> Position -> IdeM (IdeResult [Hover]) -type SymbolProvider = Uri -> IdeM (IdeResponse [DocumentSymbol]) +type SymbolProvider = Uri -> IdeM (IdeResult [DocumentSymbol]) data PluginDescriptor = PluginDescriptor { pluginId :: PluginId @@ -198,39 +198,67 @@ instance ToJSON IdePlugins where -- --------------------------------------------------------------------- -type IdeGhcM = GM.GhcModT IdeM +type IdeGhcM = GM.GhcModT IdeBase -instance MonadMTState IdeState IdeGhcM where - readMTS = lift $ lift $ lift readMTS - modifyMTS f = lift $ lift $ lift $ modifyMTS f +-- | A computation that is deferred until the module is cached. +-- Note that the module may not typecheck, in which case 'UriCacheFailed' is passed +data IdeDefer a = IdeDefer FilePath (UriCache -> a) deriving Functor +type IdeM = FreeT IdeDefer IdeBase + +type IdeBase = ReaderT ClientCapabilities (MultiThreadState IdeState) -type IdeM = ReaderT ClientCapabilities (MultiThreadState IdeState) +data IdeState = IdeState + { moduleCache :: GhcModuleCache + -- | A queue of requests to be performed once a module is loaded + , requestQueue :: Map.Map FilePath [UriCache -> IdeBase ()] + , idePlugins :: IdePlugins + , extensibleState :: !(Map.Map TypeRep Dynamic) + , ghcSession :: Maybe (IORef HscEnv) + -- The pid of this instance of hie + , idePidCache :: Int + } +instance MonadMTState IdeState IdeGhcM where + readMTS = lift $ lift $ lift readMTS + modifyMTS = lift . lift . lift . modifyMTS + instance MonadMTState IdeState IdeM where - readMTS = lift readMTS - modifyMTS = lift . modifyMTS + readMTS = lift $ lift readMTS + modifyMTS = lift . lift . modifyMTS class (Monad m) => LiftsToGhc m where liftToGhc :: m a -> IdeGhcM a +instance GM.MonadIO IdeM where + liftIO = liftIO + instance LiftsToGhc IdeM where + liftToGhc (FreeT f) = do + x <- liftToGhc f + case x of + Pure a -> return a + Free (IdeDefer fp cb) -> do + fp' <- liftIO $ canonicalizePath fp + muc <- fmap (Map.lookup fp' . uriCaches) getModuleCache + liftToGhc $ case muc of + Just uc -> cb uc + Nothing -> cb UriCacheFailed + +instance LiftsToGhc IdeBase where liftToGhc = lift . lift instance LiftsToGhc IdeGhcM where liftToGhc = id -data IdeState = IdeState - { moduleCache :: GhcModuleCache - -- | A queue of requests to be performed once a module is loaded - , requestQueue :: Map.Map FilePath [Either T.Text CachedModule -> IdeM ()] - , idePlugins :: IdePlugins - , extensibleState :: !(Map.Map TypeRep Dynamic) - , ghcSession :: Maybe (IORef HscEnv) - -- The pid of this instance of hie - , idePidCache :: Int - } +instance HasGhcModuleCache IdeGhcM where + getModuleCache = lift $ lift getModuleCache + setModuleCache = lift . lift . setModuleCache instance HasGhcModuleCache IdeM where + getModuleCache = lift getModuleCache + setModuleCache = lift . setModuleCache + +instance HasGhcModuleCache IdeBase where getModuleCache = do tvar <- lift ask state <- liftIO $ readTVarIO tvar @@ -239,12 +267,6 @@ instance HasGhcModuleCache IdeM where tvar <- lift ask liftIO $ atomically $ modifyTVar' tvar (\st -> st { moduleCache = mc }) -instance HasGhcModuleCache IdeGhcM where - getModuleCache = lift . lift $ getModuleCache - setModuleCache = lift . lift . setModuleCache - - - -- --------------------------------------------------------------------- @@ -290,83 +312,12 @@ instance (Monad m) => Monad (IdeResultT m) where instance MonadTrans IdeResultT where lift m = IdeResultT (fmap IdeResultOk m) --- | The IDE response, which wraps around an IdeResult that may be deferred. --- Used mostly in IdeM. -data IdeResponse' m a = IdeResponseDeferred FilePath (CachedModule -> m (IdeResponse' m a)) - | IdeResponseResult (IdeResult a) - -type IdeResponse a = IdeResponse' IdeM a - -pattern IdeResponseOk :: a -> IdeResponse' m a -pattern IdeResponseOk a = IdeResponseResult (IdeResultOk a) -pattern IdeResponseFail :: IdeError -> IdeResponse' m a -pattern IdeResponseFail err = IdeResponseResult (IdeResultFail err) - -instance (Show a) => Show (IdeResponse' m a) where - show (IdeResponseResult x) = show x - show (IdeResponseDeferred fp _) = "Deferred response waiting on " ++ fp - -instance (Eq a) => Eq (IdeResponse' m a) where - (IdeResponseResult x) == (IdeResponseResult y) = x == y - _ == _ = False - -instance Monad m => Functor (IdeResponse' m) where - fmap f (IdeResponseResult (IdeResultOk x)) = IdeResponseOk (f x) - fmap _ (IdeResponseResult (IdeResultFail err)) = IdeResponseFail err - fmap f (IdeResponseDeferred fp cb) = IdeResponseDeferred fp $ cb >=> (return . fmap f) - -instance Monad m => Applicative (IdeResponse' m) where - pure = return - - (IdeResponseResult (IdeResultFail err)) <*> _ = IdeResponseFail err - _ <*> (IdeResponseResult (IdeResultFail err)) = IdeResponseFail err - - (IdeResponseResult (IdeResultOk f)) <*> (IdeResponseResult (IdeResultOk x)) = IdeResponseOk (f x) - - (IdeResponseResult (IdeResultOk f)) <*> (IdeResponseDeferred fp cb) = IdeResponseDeferred fp $ fmap (fmap f) . cb - - (IdeResponseDeferred fp cb) <*> x = IdeResponseDeferred fp $ \cm -> do - f <- cb cm - pure (f <*> x) - -instance Monad m => Monad (IdeResponse' m) where - (IdeResponseResult (IdeResultOk x)) >>= f = f x - (IdeResponseDeferred fp cb) >>= f = IdeResponseDeferred fp $ \cm -> do - x <- cb cm - return $ x >>= f - (IdeResponseResult (IdeResultFail err)) >>= _ = IdeResponseFail err - return = IdeResponseOk - -newtype IdeResponseT m a = IdeResponseT { runIdeResponseT :: m (IdeResponse' m a) } - -instance Monad m => Functor (IdeResponseT m) where - fmap = liftM - -instance Monad m => Applicative (IdeResponseT m) where - pure = return - (<*>) = ap - -instance (Monad m) => Monad (IdeResponseT m) where - return = IdeResponseT . return . IdeResponseOk - - m >>= f = IdeResponseT $ do - v <- runIdeResponseT m - case v of - IdeResponseResult (IdeResultOk x) -> runIdeResponseT (f x) - IdeResponseResult (IdeResultFail err) -> return $ IdeResponseFail err - IdeResponseDeferred fp cb -> return $ IdeResponseDeferred fp $ \cm -> - runIdeResponseT $ IdeResponseT (cb cm) >>= f - -instance MonadTrans IdeResponseT where - lift m = IdeResponseT (fmap IdeResponseOk m) - -- | Error codes. Add as required data IdeErrorCode = ParameterError -- ^ Wrong parameter type | PluginError -- ^ An error returned by a plugin | InternalError -- ^ Code error (case not handled or deemed -- impossible) - | NoModuleAvailable -- ^ No typechecked module available to use | UnknownPlugin -- ^ Plugin is not registered | UnknownCommand -- ^ Command is not registered | InvalidContext -- ^ Context invalid for command @@ -385,4 +336,4 @@ data IdeError = IdeError deriving (Show,Read,Eq,Generic) instance ToJSON IdeError -instance FromJSON IdeError +instance FromJSON IdeError \ No newline at end of file diff --git a/hie-plugin-api/hie-plugin-api.cabal b/hie-plugin-api/hie-plugin-api.cabal index 74362c104..af5ebfb42 100644 --- a/hie-plugin-api/hie-plugin-api.cabal +++ b/hie-plugin-api/hie-plugin-api.cabal @@ -38,6 +38,7 @@ library , directory , filepath , fingertree + , free , ghc , ghc-mod-core >= 5.9.0.0 , haskell-lsp >= 0.5 diff --git a/src/Haskell/Ide/Engine/Dispatcher.hs b/src/Haskell/Ide/Engine/Dispatcher.hs index e95fff03c..b81f4ab0e 100644 --- a/src/Haskell/Ide/Engine/Dispatcher.hs +++ b/src/Haskell/Ide/Engine/Dispatcher.hs @@ -3,7 +3,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} module Haskell.Ide.Engine.Dispatcher ( dispatcherP @@ -20,7 +20,6 @@ import Control.Monad import Control.Monad.IO.Class import Control.Monad.Reader import Control.Monad.STM -import qualified Data.Aeson as J import qualified Data.Text as T import qualified Data.Map as Map import qualified Data.Set as S @@ -32,6 +31,7 @@ import Haskell.Ide.Engine.Types import Haskell.Ide.Engine.Monad import qualified Language.Haskell.LSP.Types as J import qualified Language.Haskell.LSP.Types.Capabilities as J +import System.Directory data DispatcherEnv = DispatcherEnv { cancelReqsTVar :: !(TVar (S.Set J.LspId)) @@ -66,8 +66,7 @@ dispatcherP inChan plugins ghcModOptions env errorHandler callbackHandler caps = ghcDispatcher env errorHandler callbackHandler ghcChan runIdeDisp = do stateVar <- readMVar stateVarVar - flip runReaderT stateVar $ flip runReaderT caps $ - ideDispatcher env errorHandler callbackHandler ideChan + ideDispatcher stateVar caps env errorHandler callbackHandler ideChan runMainDisp = mainDispatcher inChan ghcChan ideChan runGhcDisp `race_` runIdeDisp `race_` runMainDisp @@ -81,46 +80,36 @@ mainDispatcher inChan ghcChan ideChan = forever $ do Left r -> atomically $ writeTChan ideChan r -ideDispatcher :: forall void m. DispatcherEnv -> ErrorHandler -> CallbackHandler m -> TChan (IdeRequest m) -> IdeM void -ideDispatcher env errorHandler callbackHandler pin = forever $ do - debugm "ideDispatcher: top of loop" - (IdeRequest tn lid callback action) <- liftIO $ atomically $ readTChan pin - debugm $ "ideDispatcher: got request " ++ show tn ++ " with id: " ++ show lid - checkCancelled env lid errorHandler $ do - response <- action - handleResponse lid callback response - - where handleResponse lid callback response = - -- Need to check cancellation twice since cancellation - -- request might have come in during the action - checkCancelled env lid errorHandler $ case response of - IdeResponseResult (IdeResultOk x) -> liftIO $ do - completedReq env lid - callbackHandler callback x - IdeResponseResult (IdeResultFail (IdeError code msg _)) -> liftIO $ do - completedReq env lid - case code of - -- TODO: This isn't actually an internal error - NoModuleAvailable -> errorHandler lid J.InternalError msg - _ -> errorHandler lid J.InternalError msg - IdeResponseDeferred fp cacheCb -> handleDeferred lid fp cacheCb callback - - handleDeferred lid fp cacheCb actualCb = queueAction fp $ \case - Right cm -> do - cacheResponse <- cacheCb cm - handleResponse lid actualCb cacheResponse - Left err -> - handleResponse lid actualCb (IdeResponseFail (IdeError NoModuleAvailable err J.Null)) - - queueAction :: FilePath -> (Either T.Text CachedModule -> IdeM ()) -> IdeM () - queueAction fp action = - lift $ modifyMTState $ \s -> - let oldQueue = requestQueue s - -- add to existing queue if possible - update Nothing = [action] - update (Just x) = action : x - newQueue = Map.alter (Just . update) fp oldQueue - in s { requestQueue = newQueue } +ideDispatcher :: forall void m. TVar IdeState -> J.ClientCapabilities + -> DispatcherEnv -> ErrorHandler -> CallbackHandler m + -> TChan (IdeRequest m) -> IO void +ideDispatcher stateVar caps env errorHandler callbackHandler pin = + flip runReaderT stateVar $ flip runReaderT caps $ forever $ do + debugm "ideDispatcher: top of loop" + (IdeRequest tn lid callback action) <- liftIO $ atomically $ readTChan pin + debugm $ "ideDispatcher: got request " ++ show tn ++ " with id: " ++ show lid + + iterT queueDeferred $ + checkCancelled env lid errorHandler $ do + result <- action + checkCancelled env lid errorHandler $ liftIO $ do + completedReq env lid + case result of + IdeResultOk x -> callbackHandler callback x + IdeResultFail (IdeError _ msg _) -> errorHandler lid J.InternalError msg + + where queueDeferred (IdeDefer fp cacheCb) = do + uri' <- liftIO $ canonicalizePath fp + muc <- fmap (Map.lookup uri' . uriCaches) getModuleCache + case muc of + Just uc -> cacheCb uc + Nothing -> lift $ modifyMTState $ \s -> + let oldQueue = requestQueue s + -- add to existing queue if possible + update Nothing = [cacheCb] + update (Just x) = cacheCb : x + newQueue = Map.alter (Just . update) fp oldQueue + in s { requestQueue = newQueue } ghcDispatcher :: forall void m. DispatcherEnv -> ErrorHandler -> CallbackHandler m -> TChan (GhcRequest m) -> IdeGhcM void ghcDispatcher env@DispatcherEnv{docVersionTVar} errorHandler callbackHandler pin = forever $ do @@ -140,11 +129,9 @@ ghcDispatcher env@DispatcherEnv{docVersionTVar} errorHandler callbackHandler pin result <- runner action liftIO $ case result of IdeResultOk x -> callbackHandler callback x - IdeResultFail err@(IdeError code msg _) -> + IdeResultFail err@(IdeError _ msg _) -> case mid of - Just lid -> case code of - NoModuleAvailable -> errorHandler lid J.ParseError msg - _ -> errorHandler lid J.InternalError msg + Just lid -> errorHandler lid J.InternalError msg Nothing -> debugm $ "ghcDispatcher:Got error for a request: " ++ show err let runIfVersionMatch = case mver of diff --git a/src/Haskell/Ide/Engine/LSP/CodeActions.hs b/src/Haskell/Ide/Engine/LSP/CodeActions.hs index 8b876434f..85fb7761e 100644 --- a/src/Haskell/Ide/Engine/LSP/CodeActions.hs +++ b/src/Haskell/Ide/Engine/LSP/CodeActions.hs @@ -39,8 +39,8 @@ handleCodeActionReq tn req = do let getProvider p = pluginCodeActionProvider p <*> return (pluginId p) getProviders = do - IdePlugins m <- lift getPlugins - return $ IdeResponseOk $ mapMaybe getProvider $ toList m + IdePlugins m <- getPlugins + return $ IdeResultOk $ mapMaybe getProvider $ toList m providersCb providers = let reqs = map (\f -> f docId maybeRootDir range context) providers diff --git a/src/Haskell/Ide/Engine/LSP/Reactor.hs b/src/Haskell/Ide/Engine/LSP/Reactor.hs index 474f82fba..86cd721c2 100644 --- a/src/Haskell/Ide/Engine/LSP/Reactor.hs +++ b/src/Haskell/Ide/Engine/LSP/Reactor.hs @@ -95,7 +95,7 @@ writePluginReq req lid = do writeTChan cin req -- | Execute multiple ide requests sequentially -makeRequests :: [IdeM (IdeResponse a)] -- ^ The requests to make +makeRequests :: [IdeM (IdeResult a)] -- ^ The requests to make -> TrackingNumber -> J.LspId -> ([a] -> R ()) -- ^ Callback with the request inputs and results diff --git a/src/Haskell/Ide/Engine/Plugin/ApplyRefact.hs b/src/Haskell/Ide/Engine/Plugin/ApplyRefact.hs index 11f061968..59320d4fd 100644 --- a/src/Haskell/Ide/Engine/Plugin/ApplyRefact.hs +++ b/src/Haskell/Ide/Engine/Plugin/ApplyRefact.hs @@ -241,7 +241,7 @@ applyHint fp mhint fileMap = do return diff -- | Gets HLint ideas for -getIdeas :: FilePath -> Maybe OneHint -> ExceptT String IdeM [Idea] +getIdeas :: MonadIO m => FilePath -> Maybe OneHint -> ExceptT String m [Idea] getIdeas lintFile mhint = do let hOpts = hlintOpts lintFile (oneHintPos <$> mhint) ideas <- runHlint lintFile hOpts @@ -263,7 +263,7 @@ hlintOpts lintFile mpos = opts = maybe "" posOpt mpos in [lintFile, "--quiet", "--refactor", "--refactor-options=" ++ opts ] -runHlint :: FilePath -> [String] -> ExceptT String IdeM [Idea] +runHlint :: MonadIO m => FilePath -> [String] -> ExceptT String m [Idea] runHlint fp args = do (flags,classify,hint) <- liftIO $ argsSettings args let myflags = flags { hseFlags = (hseFlags flags) { extensions = (EnableExtension TypeApplications:extensions (hseFlags flags))}} @@ -277,7 +277,7 @@ showParseError (Hlint.ParseError location message content) = -- --------------------------------------------------------------------- codeActionProvider :: CodeActionProvider -codeActionProvider plId docId _ _ context = IdeResponseOk <$> hlintActions +codeActionProvider plId docId _ _ context = IdeResultOk <$> hlintActions where hlintActions :: IdeM [LSP.CodeAction] diff --git a/src/Haskell/Ide/Engine/Plugin/GhcMod.hs b/src/Haskell/Ide/Engine/Plugin/GhcMod.hs index 2576599f2..ec80ede3f 100644 --- a/src/Haskell/Ide/Engine/Plugin/GhcMod.hs +++ b/src/Haskell/Ide/Engine/Plugin/GhcMod.hs @@ -206,7 +206,7 @@ setTypecheckedModule uri = Nothing -> do debugm $ "setTypecheckedModule: Didn't get typechecked module for: " ++ show fp - failModule fp (T.unlines errs) + failModule fp return $ IdeResultOk (diags,errs) Just tm -> do @@ -336,8 +336,7 @@ splitCaseCmd' uri newPos = return $ oldToNewPositions checkedModule wEdit Nothing -> return mempty Nothing -> return mempty - ModuleFailed errText -> return $ IdeResultFail $ IdeError PluginError (T.append "hie-ghc-mod: " errText) Null - ModuleLoading -> return $ IdeResultOk mempty + _ -> return $ IdeResultOk mempty where -- | Transform all ranges in a WorkspaceEdit from old to new positions. @@ -420,7 +419,7 @@ codeActionProvider' supportsDocChanges _ docId _ _ context = redundantTerms = mapMaybe getRedundantImports diags redundantActions = concatMap (uncurry mkRedundantImportActions) redundantTerms typedHoleActions = concatMap mkTypedHoleActions (mapMaybe getTypedHoles diags) - in return $ IdeResponseOk (renameActions ++ redundantActions ++ typedHoleActions) + in return $ IdeResultOk (renameActions ++ redundantActions ++ typedHoleActions) where @@ -584,9 +583,9 @@ extractHoleSubstitutions diag -- --------------------------------------------------------------------- hoverProvider :: HoverProvider -hoverProvider doc pos = runIdeResponseT $ do - info' <- IdeResponseT $ IdeResponseResult <$> newTypeCmd pos doc - names' <- IdeResponseT $ Hie.getSymbolsAtPoint doc pos +hoverProvider doc pos = runIdeResultT $ do + info' <- IdeResultT $ newTypeCmd pos doc + names' <- IdeResultT $ Hie.getSymbolsAtPoint doc pos let f = (==) `on` (Hie.showName . snd) f' = compare `on` (Hie.showName . snd) @@ -620,91 +619,92 @@ hoverProvider doc pos = runIdeResponseT $ do data Decl = Decl LSP.SymbolKind (Located RdrName) [Decl] SrcSpan | Import LSP.SymbolKind (Located ModuleName) [Decl] SrcSpan -symbolProvider :: Uri -> IdeM (IdeResponse [LSP.DocumentSymbol]) -symbolProvider uri = pluginGetFileResponse "ghc-mod symbolProvider: " uri $ \file -> withCachedModule file $ \cm -> do - let tm = tcMod cm - hsMod = unLoc $ pm_parsed_source $ tm_parsed_module tm - imports = hsmodImports hsMod - imps = concatMap goImport imports - decls = concatMap go $ hsmodDecls hsMod - - go :: LHsDecl GM.GhcPs -> [Decl] - go (L l (TyClD FamDecl { tcdFam = FamilyDecl { fdLName = n } })) = pure (Decl LSP.SkClass n [] l) - go (L l (TyClD SynDecl { tcdLName = n })) = pure (Decl LSP.SkClass n [] l) - go (L l (TyClD DataDecl { tcdLName = n, tcdDataDefn = HsDataDefn { dd_cons = cons } })) = - pure (Decl LSP.SkClass n (concatMap processCon cons) l) - go (L l (TyClD ClassDecl { tcdLName = n, tcdSigs = sigs, tcdATs = fams })) = - pure (Decl LSP.SkInterface n children l) - where children = famDecls ++ sigDecls - famDecls = concatMap (go . fmap (TyClD . FamDecl)) fams - sigDecls = concatMap processSig sigs - - go (L l (ValD FunBind { fun_id = ln, fun_matches = MG { mg_alts = llms } })) = - pure (Decl LSP.SkFunction ln wheres l) - where - wheres = concatMap (gomatch . unLoc) (unLoc llms) - gomatch Match { m_grhss = GRHSs { grhssLocalBinds = lbs } } = golbs (unLoc lbs) - golbs (HsValBinds (ValBindsIn lhsbs _ )) = concatMap (go . fmap ValD) lhsbs - golbs _ = [] - - go (L l (ValD PatBind { pat_lhs = p })) = - map (\n -> Decl LSP.SkVariable n [] l) $ hsNamessRdr p - go (L l (ForD ForeignImport { fd_name = n })) = pure (Decl LSP.SkFunction n [] l) - go _ = [] - - processSig :: LSig GM.GhcPs -> [Decl] - processSig (L l (ClassOpSig False names _)) = - map (\n -> Decl LSP.SkMethod n [] l) names - processSig _ = [] - - processCon :: LConDecl GM.GhcPs -> [Decl] - processCon (L l ConDeclGADT { con_names = names }) = - map (\n -> Decl LSP.SkConstructor n [] l) names - processCon (L l ConDeclH98 { con_name = name, con_details = dets }) = - pure (Decl LSP.SkConstructor name xs l) - where - f (L fl ln) = Decl LSP.SkField ln [] fl - xs = case dets of - RecCon (L _ rs) -> concatMap (map (f . fmap rdrNameFieldOcc) - . cd_fld_names - . unLoc) rs - _ -> [] - - goImport :: LImportDecl GM.GhcPs -> [Decl] - goImport (L l ImportDecl { ideclName = lmn, ideclAs = as, ideclHiding = meis }) = pure im - where - im = Import imKind lmn xs l - imKind - | isJust as = LSP.SkNamespace - | otherwise = LSP.SkModule - xs = case meis of - Just (False, eis) -> concatMap f (unLoc eis) - _ -> [] - f (L l' (IEVar n)) = pure (Decl LSP.SkFunction (ieLWrappedName n) [] l') - f (L l' (IEThingAbs n)) = pure (Decl LSP.SkClass (ieLWrappedName n) [] l') - f (L l' (IEThingAll n)) = pure (Decl LSP.SkClass (ieLWrappedName n) [] l') - f (L l' (IEThingWith n _ vars fields)) = - let funcDecls = map (\n' -> Decl LSP.SkFunction (ieLWrappedName n') [] (getLoc n')) vars - fieldDecls = map (\f' -> Decl LSP.SkField (flSelector <$> f') [] (getLoc f')) fields - children = funcDecls ++ fieldDecls - in pure (Decl LSP.SkClass (ieLWrappedName n) children l') - f _ = [] - - declsToSymbolInf :: Decl -> IdeM [LSP.DocumentSymbol] - declsToSymbolInf (Decl kind (L nl rdrName) children l) = - declToSymbolInf' l kind nl (Hie.showName rdrName) children - declsToSymbolInf (Import kind (L nl modName) children l) = - declToSymbolInf' l kind nl (Hie.showName modName) children - - declToSymbolInf' :: SrcSpan -> LSP.SymbolKind -> SrcSpan -> T.Text -> [Decl] -> IdeM [LSP.DocumentSymbol] - declToSymbolInf' ss kind nss name children = do - childrenSymbols <- concat <$> mapM declsToSymbolInf children - case (srcSpan2Range ss, srcSpan2Range nss) of - (Right r, Right selR) -> - let chList = Just (LSP.List childrenSymbols) - in return $ pure $ - LSP.DocumentSymbol name (Just "") kind Nothing r selR chList - _ -> return childrenSymbols - - symInfs <- concat <$> mapM declsToSymbolInf (imps ++ decls) - return $ IdeResponseOk symInfs +symbolProvider :: Uri -> IdeM (IdeResult [LSP.DocumentSymbol]) +symbolProvider uri = pluginGetFile "ghc-mod symbolProvider: " uri $ + \file -> withCachedModule file (IdeResultOk []) $ \cm -> do + let tm = tcMod cm + hsMod = unLoc $ pm_parsed_source $ tm_parsed_module tm + imports = hsmodImports hsMod + imps = concatMap goImport imports + decls = concatMap go $ hsmodDecls hsMod + + go :: LHsDecl GM.GhcPs -> [Decl] + go (L l (TyClD FamDecl { tcdFam = FamilyDecl { fdLName = n } })) = pure (Decl LSP.SkClass n [] l) + go (L l (TyClD SynDecl { tcdLName = n })) = pure (Decl LSP.SkClass n [] l) + go (L l (TyClD DataDecl { tcdLName = n, tcdDataDefn = HsDataDefn { dd_cons = cons } })) = + pure (Decl LSP.SkClass n (concatMap processCon cons) l) + go (L l (TyClD ClassDecl { tcdLName = n, tcdSigs = sigs, tcdATs = fams })) = + pure (Decl LSP.SkInterface n children l) + where children = famDecls ++ sigDecls + famDecls = concatMap (go . fmap (TyClD . FamDecl)) fams + sigDecls = concatMap processSig sigs + + go (L l (ValD FunBind { fun_id = ln, fun_matches = MG { mg_alts = llms } })) = + pure (Decl LSP.SkFunction ln wheres l) + where + wheres = concatMap (gomatch . unLoc) (unLoc llms) + gomatch Match { m_grhss = GRHSs { grhssLocalBinds = lbs } } = golbs (unLoc lbs) + golbs (HsValBinds (ValBindsIn lhsbs _ )) = concatMap (go . fmap ValD) lhsbs + golbs _ = [] + + go (L l (ValD PatBind { pat_lhs = p })) = + map (\n -> Decl LSP.SkVariable n [] l) $ hsNamessRdr p + go (L l (ForD ForeignImport { fd_name = n })) = pure (Decl LSP.SkFunction n [] l) + go _ = [] + + processSig :: LSig GM.GhcPs -> [Decl] + processSig (L l (ClassOpSig False names _)) = + map (\n -> Decl LSP.SkMethod n [] l) names + processSig _ = [] + + processCon :: LConDecl GM.GhcPs -> [Decl] + processCon (L l ConDeclGADT { con_names = names }) = + map (\n -> Decl LSP.SkConstructor n [] l) names + processCon (L l ConDeclH98 { con_name = name, con_details = dets }) = + pure (Decl LSP.SkConstructor name xs l) + where + f (L fl ln) = Decl LSP.SkField ln [] fl + xs = case dets of + RecCon (L _ rs) -> concatMap (map (f . fmap rdrNameFieldOcc) + . cd_fld_names + . unLoc) rs + _ -> [] + + goImport :: LImportDecl GM.GhcPs -> [Decl] + goImport (L l ImportDecl { ideclName = lmn, ideclAs = as, ideclHiding = meis }) = pure im + where + im = Import imKind lmn xs l + imKind + | isJust as = LSP.SkNamespace + | otherwise = LSP.SkModule + xs = case meis of + Just (False, eis) -> concatMap f (unLoc eis) + _ -> [] + f (L l' (IEVar n)) = pure (Decl LSP.SkFunction (ieLWrappedName n) [] l') + f (L l' (IEThingAbs n)) = pure (Decl LSP.SkClass (ieLWrappedName n) [] l') + f (L l' (IEThingAll n)) = pure (Decl LSP.SkClass (ieLWrappedName n) [] l') + f (L l' (IEThingWith n _ vars fields)) = + let funcDecls = map (\n' -> Decl LSP.SkFunction (ieLWrappedName n') [] (getLoc n')) vars + fieldDecls = map (\f' -> Decl LSP.SkField (flSelector <$> f') [] (getLoc f')) fields + children = funcDecls ++ fieldDecls + in pure (Decl LSP.SkClass (ieLWrappedName n) children l') + f _ = [] + + declsToSymbolInf :: Decl -> IdeM [LSP.DocumentSymbol] + declsToSymbolInf (Decl kind (L nl rdrName) children l) = + declToSymbolInf' l kind nl (Hie.showName rdrName) children + declsToSymbolInf (Import kind (L nl modName) children l) = + declToSymbolInf' l kind nl (Hie.showName modName) children + + declToSymbolInf' :: SrcSpan -> LSP.SymbolKind -> SrcSpan -> T.Text -> [Decl] -> IdeM [LSP.DocumentSymbol] + declToSymbolInf' ss kind nss name children = do + childrenSymbols <- concat <$> mapM declsToSymbolInf children + case (srcSpan2Range ss, srcSpan2Range nss) of + (Right r, Right selR) -> + let chList = Just (LSP.List childrenSymbols) + in return $ pure $ + LSP.DocumentSymbol name (Just "") kind Nothing r selR chList + _ -> return childrenSymbols + + symInfs <- concat <$> mapM declsToSymbolInf (imps ++ decls) + return $ IdeResultOk symInfs \ No newline at end of file diff --git a/src/Haskell/Ide/Engine/Plugin/HaRe.hs b/src/Haskell/Ide/Engine/Plugin/HaRe.hs index c78992705..c16f47172 100644 --- a/src/Haskell/Ide/Engine/Plugin/HaRe.hs +++ b/src/Haskell/Ide/Engine/Plugin/HaRe.hs @@ -292,21 +292,21 @@ hoist f a = codeActionProvider :: CodeActionProvider codeActionProvider pId docId _ (J.Range pos _) _ = - pluginGetFileResponse "HaRe codeActionProvider: " (docId ^. J.uri) $ \file -> - withCachedModuleDefault file (Just (IdeResponseOk mempty)) $ \cm -> do + pluginGetFile "HaRe codeActionProvider: " (docId ^. J.uri) $ \file -> + withCachedModule file (IdeResultOk mempty) $ \cm -> do let symbols = getArtifactsAtPos pos (defMap cm) debugm $ show $ map (Hie.showName . snd) symbols if not (null symbols) then let name = Hie.showName $ snd $ head symbols - in IdeResponseOk <$> sequence [ + in IdeResultOk <$> sequence [ mkLiftOneAction name , mkLiftTopAction name , mkDemoteAction name , mkDeleteAction name , mkDuplicateAction name ] - else return (IdeResponseOk []) + else return (IdeResultOk []) where mkLiftOneAction name = do diff --git a/src/Haskell/Ide/Engine/Plugin/Haddock.hs b/src/Haskell/Ide/Engine/Plugin/Haddock.hs index f3b4d98eb..724ca83df 100644 --- a/src/Haskell/Ide/Engine/Plugin/Haddock.hs +++ b/src/Haskell/Ide/Engine/Plugin/Haddock.hs @@ -217,9 +217,9 @@ renderMarkDown = removeInner x = T.replace "```" "" $ T.replace "```haskell" "" x hoverProvider :: HoverProvider -hoverProvider doc pos = runIdeResponseT $ do - df <- IdeResponseT $ getDynFlags doc - names' <- IdeResponseT $ getSymbolsAtPoint doc pos +hoverProvider doc pos = runIdeResultT $ do + df <- IdeResultT $ getDynFlags doc + names' <- IdeResultT $ getSymbolsAtPoint doc pos let names = mapMaybe pickName $ groupBy f $ sortBy f' names' docs <- forM names $ \(_,name) -> do let sname = showName name diff --git a/src/Haskell/Ide/Engine/Plugin/HieExtras.hs b/src/Haskell/Ide/Engine/Plugin/HieExtras.hs index 3a1865340..df8b3c335 100644 --- a/src/Haskell/Ide/Engine/Plugin/HieExtras.hs +++ b/src/Haskell/Ide/Engine/Plugin/HieExtras.hs @@ -32,7 +32,6 @@ import Exception import FastString import Finder import GHC -import qualified GhcMod.Error as GM import qualified GhcMod.LightGhc as GM import Haskell.Ide.Engine.ArtifactMap import Haskell.Ide.Engine.MonadFunctions @@ -52,10 +51,12 @@ import SrcLoc import TcEnv import Var -getDynFlags :: Uri -> IdeM (IdeResponse DynFlags) +getDynFlags :: Uri -> IdeM (IdeResult DynFlags) getDynFlags uri = - pluginGetFileResponse "getDynFlags: " uri $ \fp -> - withCachedModule fp (return . IdeResponseOk . ms_hspp_opts . pm_mod_summary . tm_parsed_module . tcMod) + pluginGetFile "getDynFlags: " uri $ \fp -> + withCachedModule fp (IdeResultFail err) + (return . IdeResultOk . ms_hspp_opts . pm_mod_summary . tm_parsed_module . tcMod) + where err = IdeError InternalError "Unable to get DynFlags" Null -- --------------------------------------------------------------------- @@ -247,31 +248,23 @@ instance ModuleCache CachedCompletions where , qualCompls = quals } -getCompletions :: Uri -> (T.Text, T.Text) -> IdeM (IdeResponse [J.CompletionItem]) -getCompletions uri (qualifier, ident) = pluginGetFileResponse "getCompletions: " uri $ \file -> - let handlers = - [ GM.GHandler $ \(ex :: SomeException) -> - return $ IdeResponseFail $ IdeError PluginError - (T.pack $ "getCompletions" <> ": " <> (show ex)) - Null - ] - in flip GM.gcatches handlers $ do - -- debugm $ "got prefix" ++ show (qualifier, ident) - let enteredQual = if T.null qualifier then "" else qualifier <> "." - fullPrefix = enteredQual <> ident - withCachedModuleAndData file $ \_ CC { allModNamesAsNS, unqualCompls, qualCompls } -> - let - filtModNameCompls = map mkModCompl - $ mapMaybe (T.stripPrefix enteredQual) - $ Fuzzy.simpleFilter fullPrefix allModNamesAsNS - - filtCompls = Fuzzy.filterBy label ident compls - where - compls = if T.null qualifier - then unqualCompls - else Map.findWithDefault [] qualifier qualCompls +getCompletions :: Uri -> (T.Text, T.Text) -> IdeM (IdeResult [J.CompletionItem]) +getCompletions uri (qualifier, ident) = pluginGetFile "getCompletions: " uri $ \file -> + let enteredQual = if T.null qualifier then "" else qualifier <> "." + fullPrefix = enteredQual <> ident + in withCachedModuleAndData file (IdeResultOk []) $ \_ CC { allModNamesAsNS, unqualCompls, qualCompls } -> + let + filtModNameCompls = map mkModCompl + $ mapMaybe (T.stripPrefix enteredQual) + $ Fuzzy.simpleFilter fullPrefix allModNamesAsNS + + filtCompls = Fuzzy.filterBy label ident compls + where + compls = if T.null qualifier + then unqualCompls + else Map.findWithDefault [] qualifier qualCompls - in return $ IdeResponseOk $ filtModNameCompls ++ map mkCompl filtCompls + in return $ IdeResultOk $ filtModNameCompls ++ map mkCompl filtCompls -- --------------------------------------------------------------------- @@ -288,9 +281,9 @@ getTypeForName n = do -- --------------------------------------------------------------------- -getSymbolsAtPoint :: Uri -> Position -> IdeM (IdeResponse [(Range, Name)]) -getSymbolsAtPoint uri pos = pluginGetFileResponse "getSymbolsAtPoint: " uri $ \file -> - withCachedModule file $ return . IdeResponseOk . getSymbolsAtPointPure pos +getSymbolsAtPoint :: Uri -> Position -> IdeM (IdeResult [(Range, Name)]) +getSymbolsAtPoint uri pos = pluginGetFile "getSymbolsAtPoint: " uri $ \file -> + withCachedModule file (IdeResultOk mempty) $ return . IdeResultOk . getSymbolsAtPointPure pos getSymbolsAtPointPure :: Position -> CachedModule -> [(Range,Name)] getSymbolsAtPointPure pos cm = maybe [] (`getArtifactsAtPos` locMap cm) $ newPosToOld cm pos @@ -308,17 +301,17 @@ symbolFromTypecheckedModule lm pos = -- | Find the references in the given doc, provided it has been -- loaded. If not, return the empty list. -getReferencesInDoc :: Uri -> Position -> IdeM (IdeResponse [J.DocumentHighlight]) +getReferencesInDoc :: Uri -> Position -> IdeM (IdeResult [J.DocumentHighlight]) getReferencesInDoc uri pos = - pluginGetFileResponse "getReferencesInDoc: " uri $ \file -> - withCachedModuleAndDataDefault file (Just (IdeResponseOk [])) $ + pluginGetFile "getReferencesInDoc: " uri $ \file -> + withCachedModuleAndData file (IdeResultOk []) $ \cm NMD{inverseNameMap} -> do let lm = locMap cm pm = tm_parsed_module $ tcMod cm cfile = ml_hs_file $ ms_location $ pm_mod_summary pm mpos = newPosToOld cm pos case mpos of - Nothing -> return $ IdeResponseOk [] + Nothing -> return $ IdeResultOk [] Just pos' -> return $ fmap concat $ forM (getArtifactsAtPos pos' lm) $ \(_,name) -> do let usages = fromMaybe [] $ Map.lookup name inverseNameMap @@ -362,9 +355,9 @@ getModule df n = do -- --------------------------------------------------------------------- -- | Return the definition -findDef :: Uri -> Position -> IdeM (IdeResponse [Location]) -findDef uri pos = pluginGetFileResponse "findDef: " uri $ \file -> - withCachedModuleDefault file (Just (IdeResponseOk [])) (\cm -> do +findDef :: Uri -> Position -> IdeM (IdeResult [Location]) +findDef uri pos = pluginGetFile "findDef: " uri $ \file -> + withCachedModule file (IdeResultOk []) (\cm -> do let rfm = revMap cm lm = locMap cm mm = moduleMap cm @@ -373,33 +366,33 @@ findDef uri pos = pluginGetFileResponse "findDef: " uri $ \file -> case (\x -> Just $ getArtifactsAtPos x mm) =<< oldPos of Just ((_,mn):_) -> gotoModule rfm mn _ -> case symbolFromTypecheckedModule lm =<< oldPos of - Nothing -> return $ IdeResponseOk [] + Nothing -> return $ IdeResultOk [] Just (_, n) -> case nameSrcSpan n of - UnhelpfulSpan _ -> return $ IdeResponseOk [] + UnhelpfulSpan _ -> return $ IdeResultOk [] realSpan -> do res <- srcSpan2Loc rfm realSpan case res of Right l@(J.Location luri range) -> case uriToFilePath luri of - Nothing -> return $ IdeResponseOk [l] + Nothing -> return $ IdeResultOk [l] Just fp -> do mcm' <- getCachedModule fp case mcm' of ModuleCached cm' _ -> case oldRangeToNew cm' range of - Just r -> return $ IdeResponseOk [J.Location luri r] - Nothing -> return $ IdeResponseOk [l] - _ -> return $ IdeResponseOk [l] + Just r -> return $ IdeResultOk [J.Location luri r] + Nothing -> return $ IdeResultOk [l] + _ -> return $ IdeResultOk [l] Left x -> do debugm "findDef: name srcspan not found/valid" - pure (IdeResponseFail + pure (IdeResultFail (IdeError PluginError ("hare:findDef" <> ": \"" <> x <> "\"") Null))) where - gotoModule :: (FilePath -> FilePath) -> ModuleName -> IdeM (IdeResponse [Location]) + gotoModule :: (FilePath -> FilePath) -> ModuleName -> IdeM (IdeResult [Location]) gotoModule rfm mn = do - + hscEnvRef <- ghcSession <$> readMTS mHscEnv <- liftIO $ traverse readIORef hscEnvRef @@ -415,8 +408,8 @@ findDef uri pos = pluginGetFileResponse "findDef: " uri $ \file -> let r = Range (Position 0 0) (Position 0 0) loc = Location (filePathToUri fp) r - return (IdeResponseOk [loc]) - _ -> return (IdeResponseOk []) - Nothing -> return $ IdeResponseFail + return (IdeResultOk [loc]) + _ -> return (IdeResultOk []) + Nothing -> return $ IdeResultFail (IdeError PluginError "Couldn't get hscEnv when finding import" Null) diff --git a/src/Haskell/Ide/Engine/Plugin/HsImport.hs b/src/Haskell/Ide/Engine/Plugin/HsImport.hs index 434ef8daf..fc3353b5a 100644 --- a/src/Haskell/Ide/Engine/Plugin/HsImport.hs +++ b/src/Haskell/Ide/Engine/Plugin/HsImport.hs @@ -83,8 +83,8 @@ codeActionProvider plId docId _ _ context = do let relaxedTerms = map (bimap id (head . T.words)) terms relaxedRes <- mapM (bimapM return Hoogle.searchModules) relaxedTerms relaxedActions <- catMaybes <$> mapM (uncurry mkImportAction) (concatTerms relaxedRes) - return $ IdeResponseOk relaxedActions - else return $ IdeResponseOk actions + return $ IdeResultOk relaxedActions + else return $ IdeResultOk actions where concatTerms = concatMap (\(d, ts) -> map (d,) ts) diff --git a/src/Haskell/Ide/Engine/Plugin/Liquid.hs b/src/Haskell/Ide/Engine/Plugin/Liquid.hs index 238a3bfa1..1546afea8 100644 --- a/src/Haskell/Ide/Engine/Plugin/Liquid.hs +++ b/src/Haskell/Ide/Engine/Plugin/Liquid.hs @@ -184,12 +184,12 @@ liquidFileFor uri ext = hoverProvider :: HoverProvider hoverProvider uri pos = - pluginGetFileResponse "Liquid.hoverProvider: " uri $ \file -> - withCachedModuleAndDataDefault file (Just (IdeResponseOk [])) $ + pluginGetFile "Liquid.hoverProvider: " uri $ \file -> + withCachedModuleAndData file (IdeResultOk []) $ \cm () -> do merrs <- liftIO $ readVimAnnot uri case merrs of - Nothing -> return $ IdeResponseResult (IdeResultOk []) + Nothing -> return (IdeResultOk []) Just lerrs -> do let perrs = map (\le@(LE s e _) -> (lpToPos s,lpToPos e,le)) lerrs ls = getThingsAtPos cm pos perrs @@ -197,7 +197,7 @@ hoverProvider uri pos = let msgs = T.splitOn "\\n" msg msg' = J.CodeString (J.LanguageString "haskell" (T.unlines msgs)) return $ J.Hover (J.List [msg']) (Just r) - return $ IdeResponseResult (IdeResultOk hs) + return (IdeResultOk hs) -- --------------------------------------------------------------------- diff --git a/src/Haskell/Ide/Engine/Plugin/Package.hs b/src/Haskell/Ide/Engine/Plugin/Package.hs index 499bf7aaf..eecda281c 100644 --- a/src/Haskell/Ide/Engine/Plugin/Package.hs +++ b/src/Haskell/Ide/Engine/Plugin/Package.hs @@ -239,7 +239,7 @@ codeActionProvider plId docId mRootDir _ context = do res <- mapM (bimapM return Hoogle.searchPackages) pkgs actions <- catMaybes <$> mapM (uncurry mkAddPackageAction) (concatPkgs res) - return $ IdeResponseOk actions + return (IdeResultOk actions) where concatPkgs = concatMap (\(d, ts) -> map (d,) ts) diff --git a/src/Haskell/Ide/Engine/Transport/LspStdio.hs b/src/Haskell/Ide/Engine/Transport/LspStdio.hs index 2fddc8b7c..4faec86c1 100644 --- a/src/Haskell/Ide/Engine/Transport/LspStdio.hs +++ b/src/Haskell/Ide/Engine/Transport/LspStdio.hs @@ -488,13 +488,13 @@ reactor inp = do hreq :: PluginRequest R hreq = IReq tn (req ^. J.id) callback $ - pluginGetFileResponse "ReqHover:" doc $ \fp -> do + pluginGetFile "ReqHover:" doc $ \fp -> do cached <- isCached fp -- Hover requests need to be instant so don't wait -- for cached module to be loaded if cached then sequence <$> mapM (\hp -> hp doc pos) hps - else return (IdeResponseOk []) + else return (IdeResultOk []) makeRequest hreq liftIO $ U.logs "reactor:HoverRequest done" @@ -610,7 +610,7 @@ reactor inp = do rspMsg = Core.makeResponseMessage req $ origCompl & J.documentation .~ docs reactorSend $ RspCompletionItemResolve rspMsg - hreq = IReq tn (req ^. J.id) callback $ runIdeResponseT $ case mquery of + hreq = IReq tn (req ^. J.id) callback $ runIdeResultT $ case mquery of Nothing -> return Nothing Just query -> do result <- lift $ Hoogle.infoCmd' query diff --git a/src/Haskell/Ide/Engine/Types.hs b/src/Haskell/Ide/Engine/Types.hs index 116243e60..2d2e59342 100644 --- a/src/Haskell/Ide/Engine/Types.hs +++ b/src/Haskell/Ide/Engine/Types.hs @@ -26,7 +26,7 @@ pattern GReq :: TrackingNumber -> PluginRequest m pattern GReq a b c d e f = Right (GhcRequest a b c d e f) -pattern IReq :: TrackingNumber -> J.LspId -> RequestCallback m a -> IdeM (IdeResponse a) -> Either (IdeRequest m) b +pattern IReq :: TrackingNumber -> J.LspId -> RequestCallback m a -> IdeM (IdeResult a) -> Either (IdeRequest m) b pattern IReq a b c d = Left (IdeRequest a b c d) type PluginRequest m = Either (IdeRequest m) (GhcRequest m) @@ -44,7 +44,7 @@ data IdeRequest m = forall a. IdeRequest { pureMsgNum :: TrackingNumber -- ^ Exists to facilitate logging/tracing , pureReqId :: J.LspId , pureReqCallback :: RequestCallback m a - , pureReq :: IdeM (IdeResponse a) + , pureReq :: IdeM (IdeResult a) } -- --------------------------------------------------------------------- diff --git a/test/dispatcher/Main.hs b/test/dispatcher/Main.hs index cf0de4f71..0509ae4a1 100644 --- a/test/dispatcher/Main.hs +++ b/test/dispatcher/Main.hs @@ -116,7 +116,7 @@ dispatchGhcRequest tn ctx n cin lc plugin com arg = do dispatchIdeRequest :: (Typeable a, ToJSON a) => TrackingNumber -> String -> TChan (PluginRequest IO) - -> TChan LogVal -> LspId -> IdeM (IdeResponse a) -> IO () + -> TChan LogVal -> LspId -> IdeM (IdeResult a) -> IO () dispatchIdeRequest tn ctx cin lc lid f = do let logger :: (Typeable a, ToJSON a) => RequestCallback IO a @@ -180,11 +180,11 @@ funcSpec = describe "functional dispatch" $ do let -- Model a hover request hoverReq tn idVal doc = dispatchIdeRequest tn ("IReq " ++ show idVal) cin logChan idVal $ do - pluginGetFileResponse "hoverReq" doc $ \fp -> do + pluginGetFile "hoverReq" doc $ \fp -> do cached <- isCached fp if cached - then return (IdeResponseOk Cached) - else return (IdeResponseOk NotCached) + then return (IdeResultOk Cached) + else return (IdeResultOk NotCached) unpackRes (r,Right md) = (r, fromDynJSON md) unpackRes r = error $ "unpackRes:" ++ show r @@ -314,14 +314,14 @@ funcSpec = describe "functional dispatch" $ do Nothing )) - it "instantly responds to failed modules with no cache" $ do + it "instantly responds to failed modules with no cache with the default" $ do dispatchIdeRequest 7 "req7" cin logChan (IdInt 7) $ symbolProvider testFailUri dispatchGhcRequest 8 "req8" 8 cin logChan "ghcmod" "check" (toJSON testFailUri) - (_, Left symbolError) <- atomically $ readTChan logChan - symbolError `shouldBe` (IdInt 7, Language.Haskell.LSP.Types.InternalError, "") + hr7 <- atomically $ readTChan logChan + unpackRes hr7 `shouldBe` ("req7", Just ([] :: [DocumentSymbol])) ("req8", Right diags) <- atomically $ readTChan logChan show diags `shouldBe` "((Map Uri (Set Diagnostic)),[Text])" diff --git a/test/functional/DeferredSpec.hs b/test/functional/DeferredSpec.hs index c9c358fc8..4d76afe4e 100644 --- a/test/functional/DeferredSpec.hs +++ b/test/functional/DeferredSpec.hs @@ -12,7 +12,7 @@ import qualified Data.HashMap.Strict as H import Data.Maybe import Language.Haskell.LSP.Test import Language.Haskell.LSP.Types hiding (message) -import qualified Language.Haskell.LSP.Types as LSP (error, id) +import qualified Language.Haskell.LSP.Types as LSP (id) import Test.Hspec import System.Directory import System.FilePath @@ -95,7 +95,7 @@ spec = do doc <- openDoc "FuncTestFail.hs" "haskell" symbols <- request TextDocumentDocumentSymbol (DocumentSymbolParams doc) :: Session DocumentSymbolsResponse - liftIO $ symbols ^. LSP.error `shouldNotBe` Nothing + liftIO $ symbols ^. result `shouldBe` Just (DSDocumentSymbols mempty) it "returns hints as diagnostics" $ runSession hieCommand fullCaps "test/testdata" $ do _ <- openDoc "FuncTest.hs" "haskell" @@ -162,4 +162,5 @@ spec = do diagsRsp2 <- skipManyTill anyNotification message :: Session PublishDiagnosticsNotification let (List diags2) = diagsRsp2 ^. params . diagnostics + liftIO $ show diags2 `shouldBe` "[]" diff --git a/test/unit/HaRePluginSpec.hs b/test/unit/HaRePluginSpec.hs index 6ba6fc908..79ccf71d3 100644 --- a/test/unit/HaRePluginSpec.hs +++ b/test/unit/HaRePluginSpec.hs @@ -175,29 +175,29 @@ hareSpec = do lreq = setTypecheckedModule u req = liftToGhc $ findDef u (toPos (7,8)) r <- dispatchRequestPGoto $ lreq >> req - r `shouldBe` IdeResponseOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib.hs") + r `shouldBe` IdeResultOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib.hs") (Range (toPos (6,1)) (toPos (6,9)))] let req2 = liftToGhc $ findDef u (toPos (7,20)) r2 <- dispatchRequestPGoto $ lreq >> req2 - r2 `shouldBe` IdeResponseOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") + r2 `shouldBe` IdeResultOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") (Range (toPos (5,1)) (toPos (5,2)))] it "finds definition in the same component" $ do let u = filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs" lreq = setTypecheckedModule u req = liftToGhc $ findDef u (toPos (6,5)) r <- dispatchRequestPGoto $ lreq >> req - r `shouldBe` IdeResponseOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib.hs") + r `shouldBe` IdeResultOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib.hs") (Range (toPos (6,1)) (toPos (6,9)))] it "finds local definitions" $ do let u = filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs" lreq = setTypecheckedModule u req = liftToGhc $ findDef u (toPos (7,11)) r <- dispatchRequestPGoto $ lreq >> req - r `shouldBe` IdeResponseOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") + r `shouldBe` IdeResultOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") (Range (toPos (10,9)) (toPos (10,10)))] let req2 = liftToGhc $ findDef u (toPos (10,13)) r2 <- dispatchRequestPGoto $ lreq >> req2 - r2 `shouldBe` IdeResponseOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") + r2 `shouldBe` IdeResultOk [Location (filePathToUri $ cwd "test/testdata/gototest/src/Lib2.hs") (Range (toPos (9,9)) (toPos (9,10)))]