{-# LANGUAGE DeriveAnyClass     #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GADTs              #-}
{-# LANGUAGE PackageImports     #-}
-- | A plugin that adds custom messages for use in tests
module Development.IDE.Plugin.Test
  ( TestRequest(..)
  , WaitForIdeRuleResult(..)
  , plugin
  , blockCommandDescriptor
  , blockCommandId
  ) where

import           Control.Concurrent                   (threadDelay)
import           Control.Monad
import           Control.Monad.Except                 (ExceptT (..), throwError)
import           Control.Monad.IO.Class
import           Control.Monad.STM
import           Control.Monad.Trans.Class            (MonadTrans (lift))
import           Data.Aeson                           (FromJSON (parseJSON),
                                                       ToJSON (toJSON), Value)
import qualified Data.Aeson.Types                     as A
import           Data.Bifunctor
import           Data.CaseInsensitive                 (CI, original)
import qualified Data.HashMap.Strict                  as HM
import           Data.Maybe                           (isJust)
import           Data.Proxy
import           Data.String
import           Data.Text                            (Text, pack)
import           Development.IDE.Core.OfInterest      (getFilesOfInterest)
import           Development.IDE.Core.Rules
import           Development.IDE.Core.RuleTypes
import           Development.IDE.Core.Shake
import           Development.IDE.GHC.Compat
import           Development.IDE.Graph                (Action)
import qualified Development.IDE.Graph                as Graph
import           Development.IDE.Graph.Database       (ShakeDatabase,
                                                       shakeGetBuildEdges,
                                                       shakeGetBuildStep,
                                                       shakeGetCleanKeys)
import           Development.IDE.Graph.Internal.Types (Result (resultBuilt, resultChanged, resultVisited),
                                                       Step (Step))
import qualified Development.IDE.Graph.Internal.Types as Graph
import           Development.IDE.Types.Action
import           Development.IDE.Types.HscEnvEq       (HscEnvEq (hscEnv))
import           Development.IDE.Types.Location       (fromUri)
import           GHC.Generics                         (Generic)
import           Ide.Plugin.Error
import           Ide.Types
import           Language.LSP.Protocol.Message
import           Language.LSP.Protocol.Types
import qualified Language.LSP.Server                  as LSP
import qualified "list-t" ListT
import qualified StmContainers.Map                    as STM
import           System.Time.Extra

type Age = Int
data TestRequest
    = BlockSeconds Seconds           -- ^ :: Null
    | GetInterfaceFilesDir Uri       -- ^ :: String
    | GetShakeSessionQueueCount      -- ^ :: Number
    | WaitForShakeQueue -- ^ Block until the Shake queue is empty. Returns Null
    | WaitForIdeRule String Uri      -- ^ :: WaitForIdeRuleResult
    | GetBuildKeysVisited        -- ^ :: [(String]
    | GetBuildKeysBuilt          -- ^ :: [(String]
    | GetBuildKeysChanged        -- ^ :: [(String]
    | GetBuildEdgesCount         -- ^ :: Int
    | GarbageCollectDirtyKeys CheckParents Age    -- ^ :: [String] (list of keys collected)
    | GetStoredKeys                  -- ^ :: [String] (list of keys in store)
    | GetFilesOfInterest             -- ^ :: [FilePath]
    | GetRebuildsCount               -- ^ :: Int (number of times we recompiled with GHC)
    deriving (forall x. TestRequest -> Rep TestRequest x)
-> (forall x. Rep TestRequest x -> TestRequest)
-> Generic TestRequest
forall x. Rep TestRequest x -> TestRequest
forall x. TestRequest -> Rep TestRequest x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TestRequest -> Rep TestRequest x
from :: forall x. TestRequest -> Rep TestRequest x
$cto :: forall x. Rep TestRequest x -> TestRequest
to :: forall x. Rep TestRequest x -> TestRequest
Generic
    deriving anyclass (Maybe TestRequest
Value -> Parser [TestRequest]
Value -> Parser TestRequest
(Value -> Parser TestRequest)
-> (Value -> Parser [TestRequest])
-> Maybe TestRequest
-> FromJSON TestRequest
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser TestRequest
parseJSON :: Value -> Parser TestRequest
$cparseJSONList :: Value -> Parser [TestRequest]
parseJSONList :: Value -> Parser [TestRequest]
$comittedField :: Maybe TestRequest
omittedField :: Maybe TestRequest
FromJSON, [TestRequest] -> Value
[TestRequest] -> Encoding
TestRequest -> Bool
TestRequest -> Value
TestRequest -> Encoding
(TestRequest -> Value)
-> (TestRequest -> Encoding)
-> ([TestRequest] -> Value)
-> ([TestRequest] -> Encoding)
-> (TestRequest -> Bool)
-> ToJSON TestRequest
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: TestRequest -> Value
toJSON :: TestRequest -> Value
$ctoEncoding :: TestRequest -> Encoding
toEncoding :: TestRequest -> Encoding
$ctoJSONList :: [TestRequest] -> Value
toJSONList :: [TestRequest] -> Value
$ctoEncodingList :: [TestRequest] -> Encoding
toEncodingList :: [TestRequest] -> Encoding
$comitField :: TestRequest -> Bool
omitField :: TestRequest -> Bool
ToJSON)

newtype WaitForIdeRuleResult = WaitForIdeRuleResult { WaitForIdeRuleResult -> Bool
ideResultSuccess::Bool}
    deriving newtype (Maybe WaitForIdeRuleResult
Value -> Parser [WaitForIdeRuleResult]
Value -> Parser WaitForIdeRuleResult
(Value -> Parser WaitForIdeRuleResult)
-> (Value -> Parser [WaitForIdeRuleResult])
-> Maybe WaitForIdeRuleResult
-> FromJSON WaitForIdeRuleResult
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser WaitForIdeRuleResult
parseJSON :: Value -> Parser WaitForIdeRuleResult
$cparseJSONList :: Value -> Parser [WaitForIdeRuleResult]
parseJSONList :: Value -> Parser [WaitForIdeRuleResult]
$comittedField :: Maybe WaitForIdeRuleResult
omittedField :: Maybe WaitForIdeRuleResult
FromJSON, [WaitForIdeRuleResult] -> Value
[WaitForIdeRuleResult] -> Encoding
WaitForIdeRuleResult -> Bool
WaitForIdeRuleResult -> Value
WaitForIdeRuleResult -> Encoding
(WaitForIdeRuleResult -> Value)
-> (WaitForIdeRuleResult -> Encoding)
-> ([WaitForIdeRuleResult] -> Value)
-> ([WaitForIdeRuleResult] -> Encoding)
-> (WaitForIdeRuleResult -> Bool)
-> ToJSON WaitForIdeRuleResult
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: WaitForIdeRuleResult -> Value
toJSON :: WaitForIdeRuleResult -> Value
$ctoEncoding :: WaitForIdeRuleResult -> Encoding
toEncoding :: WaitForIdeRuleResult -> Encoding
$ctoJSONList :: [WaitForIdeRuleResult] -> Value
toJSONList :: [WaitForIdeRuleResult] -> Value
$ctoEncodingList :: [WaitForIdeRuleResult] -> Encoding
toEncodingList :: [WaitForIdeRuleResult] -> Encoding
$comitField :: WaitForIdeRuleResult -> Bool
omitField :: WaitForIdeRuleResult -> Bool
ToJSON)

plugin :: PluginDescriptor IdeState
plugin :: PluginDescriptor IdeState
plugin = (PluginId -> Text -> PluginDescriptor IdeState
forall ideState. PluginId -> Text -> PluginDescriptor ideState
defaultPluginDescriptor PluginId
"test" Text
"") {
    pluginHandlers = mkPluginHandler (SMethod_CustomMethod (Proxy @"test")) $ \IdeState
st PluginId
_ ->
        IdeState -> Value -> ExceptT PluginError (LspT Config IO) Value
forall {c}.
IdeState -> Value -> ExceptT PluginError (LspT c IO) Value
testRequestHandler' IdeState
st
    }
  where
      testRequestHandler' :: IdeState -> Value -> ExceptT PluginError (LspT c IO) Value
testRequestHandler' IdeState
ide Value
req
        | Just TestRequest
customReq <- (Value -> Parser TestRequest) -> Value -> Maybe TestRequest
forall a b. (a -> Parser b) -> a -> Maybe b
A.parseMaybe Value -> Parser TestRequest
forall a. FromJSON a => Value -> Parser a
parseJSON Value
req
        = LspT c IO (Either PluginError Value)
-> ExceptT PluginError (LspT c IO) Value
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (LspT c IO (Either PluginError Value)
 -> ExceptT PluginError (LspT c IO) Value)
-> LspT c IO (Either PluginError Value)
-> ExceptT PluginError (LspT c IO) Value
forall a b. (a -> b) -> a -> b
$ IdeState -> TestRequest -> LspT c IO (Either PluginError Value)
forall c.
IdeState -> TestRequest -> LspM c (Either PluginError Value)
testRequestHandler IdeState
ide TestRequest
customReq
        | Bool
otherwise
        = PluginError -> ExceptT PluginError (LspT c IO) Value
forall a. PluginError -> ExceptT PluginError (LspT c IO) a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError
        (PluginError -> ExceptT PluginError (LspT c IO) Value)
-> PluginError -> ExceptT PluginError (LspT c IO) Value
forall a b. (a -> b) -> a -> b
$ Text -> PluginError
PluginInvalidParams Text
"Cannot parse request"


testRequestHandler ::  IdeState
                -> TestRequest
                -> LSP.LspM c (Either PluginError Value)
testRequestHandler :: forall c.
IdeState -> TestRequest -> LspM c (Either PluginError Value)
testRequestHandler IdeState
_ (BlockSeconds Seconds
secs) = do
    SServerMethod ('Method_CustomMethod "ghcide/blocking/request")
-> MessageParams ('Method_CustomMethod "ghcide/blocking/request")
-> LspT c IO ()
forall (m :: Method 'ServerToClient 'Notification) (f :: * -> *)
       config.
MonadLsp config f =>
SServerMethod m -> MessageParams m -> f ()
LSP.sendNotification (Proxy "ghcide/blocking/request"
-> SServerMethod ('Method_CustomMethod "ghcide/blocking/request")
forall {f :: MessageDirection} {t :: MessageKind} (s :: Symbol).
KnownSymbol s =>
Proxy s -> SMethod ('Method_CustomMethod s)
SMethod_CustomMethod (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @"ghcide/blocking/request")) (MessageParams ('Method_CustomMethod "ghcide/blocking/request")
 -> LspT c IO ())
-> MessageParams ('Method_CustomMethod "ghcide/blocking/request")
-> LspT c IO ()
forall a b. (a -> b) -> a -> b
$
      Seconds -> Value
forall a. ToJSON a => a -> Value
toJSON Seconds
secs
    IO () -> LspT c IO ()
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT c IO ()) -> IO () -> LspT c IO ()
forall a b. (a -> b) -> a -> b
$ Seconds -> IO ()
sleep Seconds
secs
    Either PluginError Value -> LspM c (Either PluginError Value)
forall a. a -> LspT c IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Value -> Either PluginError Value
forall a b. b -> Either a b
Right Value
A.Null)
testRequestHandler IdeState
s (GetInterfaceFilesDir Uri
file) = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    let nfp :: NormalizedFilePath
nfp = NormalizedUri -> NormalizedFilePath
fromUri (NormalizedUri -> NormalizedFilePath)
-> NormalizedUri -> NormalizedFilePath
forall a b. (a -> b) -> a -> b
$ Uri -> NormalizedUri
toNormalizedUri Uri
file
    HscEnvEq
sess <- String -> IdeState -> Action HscEnvEq -> IO HscEnvEq
forall a. String -> IdeState -> Action a -> IO a
runAction String
"Test - GhcSession" IdeState
s (Action HscEnvEq -> IO HscEnvEq) -> Action HscEnvEq -> IO HscEnvEq
forall a b. (a -> b) -> a -> b
$ GhcSession -> NormalizedFilePath -> Action HscEnvEq
forall k v. IdeRule k v => k -> NormalizedFilePath -> Action v
use_ GhcSession
GhcSession NormalizedFilePath
nfp
    let hiPath :: Maybe String
hiPath = DynFlags -> Maybe String
hiDir (DynFlags -> Maybe String) -> DynFlags -> Maybe String
forall a b. (a -> b) -> a -> b
$ HscEnv -> DynFlags
hsc_dflags (HscEnv -> DynFlags) -> HscEnv -> DynFlags
forall a b. (a -> b) -> a -> b
$ HscEnvEq -> HscEnv
hscEnv HscEnvEq
sess
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Maybe String -> Value
forall a. ToJSON a => a -> Value
toJSON Maybe String
hiPath)
testRequestHandler IdeState
s TestRequest
GetShakeSessionQueueCount = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    Natural
n <- STM Natural -> IO Natural
forall a. STM a -> IO a
atomically (STM Natural -> IO Natural) -> STM Natural -> IO Natural
forall a b. (a -> b) -> a -> b
$ ActionQueue -> STM Natural
countQueue (ActionQueue -> STM Natural) -> ActionQueue -> STM Natural
forall a b. (a -> b) -> a -> b
$ ShakeExtras -> ActionQueue
actionQueue (ShakeExtras -> ActionQueue) -> ShakeExtras -> ActionQueue
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeExtras
shakeExtras IdeState
s
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Natural -> Value
forall a. ToJSON a => a -> Value
toJSON Natural
n)
testRequestHandler IdeState
s TestRequest
WaitForShakeQueue = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        Natural
n <- ActionQueue -> STM Natural
countQueue (ActionQueue -> STM Natural) -> ActionQueue -> STM Natural
forall a b. (a -> b) -> a -> b
$ ShakeExtras -> ActionQueue
actionQueue (ShakeExtras -> ActionQueue) -> ShakeExtras -> ActionQueue
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeExtras
shakeExtras IdeState
s
        Bool -> STM () -> STM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Natural
nNatural -> Natural -> Bool
forall a. Ord a => a -> a -> Bool
>Natural
0) STM ()
forall a. STM a
retry
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right Value
A.Null
testRequestHandler IdeState
s (WaitForIdeRule String
k Uri
file) = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    let nfp :: NormalizedFilePath
nfp = NormalizedUri -> NormalizedFilePath
fromUri (NormalizedUri -> NormalizedFilePath)
-> NormalizedUri -> NormalizedFilePath
forall a b. (a -> b) -> a -> b
$ Uri -> NormalizedUri
toNormalizedUri Uri
file
    Either Text Bool
success <- String
-> IdeState -> Action (Either Text Bool) -> IO (Either Text Bool)
forall a. String -> IdeState -> Action a -> IO a
runAction (String
"WaitForIdeRule " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
k String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Uri -> String
forall a. Show a => a -> String
show Uri
file) IdeState
s (Action (Either Text Bool) -> IO (Either Text Bool))
-> Action (Either Text Bool) -> IO (Either Text Bool)
forall a b. (a -> b) -> a -> b
$ CI String -> NormalizedFilePath -> Action (Either Text Bool)
parseAction (String -> CI String
forall a. IsString a => String -> a
fromString String
k) NormalizedFilePath
nfp
    let res :: Either Text WaitForIdeRuleResult
res = Bool -> WaitForIdeRuleResult
WaitForIdeRuleResult (Bool -> WaitForIdeRuleResult)
-> Either Text Bool -> Either Text WaitForIdeRuleResult
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either Text Bool
success
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ (Text -> PluginError)
-> (WaitForIdeRuleResult -> Value)
-> Either Text WaitForIdeRuleResult
-> Either PluginError Value
forall a b c d. (a -> b) -> (c -> d) -> Either a c -> Either b d
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap Text -> PluginError
PluginInvalidParams WaitForIdeRuleResult -> Value
forall a. ToJSON a => a -> Value
toJSON Either Text WaitForIdeRuleResult
res
testRequestHandler IdeState
s TestRequest
GetBuildKeysBuilt = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    [Key]
keys <- (Result -> Step) -> ShakeDatabase -> IO [Key]
getDatabaseKeys Result -> Step
resultBuilt (ShakeDatabase -> IO [Key]) -> ShakeDatabase -> IO [Key]
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeDatabase
shakeDb IdeState
s
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (Key -> String) -> [Key] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Key -> String
forall a. Show a => a -> String
show [Key]
keys
testRequestHandler IdeState
s TestRequest
GetBuildKeysChanged = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    [Key]
keys <- (Result -> Step) -> ShakeDatabase -> IO [Key]
getDatabaseKeys Result -> Step
resultChanged (ShakeDatabase -> IO [Key]) -> ShakeDatabase -> IO [Key]
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeDatabase
shakeDb IdeState
s
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (Key -> String) -> [Key] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Key -> String
forall a. Show a => a -> String
show [Key]
keys
testRequestHandler IdeState
s TestRequest
GetBuildKeysVisited = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    [Key]
keys <- (Result -> Step) -> ShakeDatabase -> IO [Key]
getDatabaseKeys Result -> Step
resultVisited (ShakeDatabase -> IO [Key]) -> ShakeDatabase -> IO [Key]
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeDatabase
shakeDb IdeState
s
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (Key -> String) -> [Key] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Key -> String
forall a. Show a => a -> String
show [Key]
keys
testRequestHandler IdeState
s TestRequest
GetBuildEdgesCount = IO (Either PluginError Value) -> LspM c (Either PluginError Value)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either PluginError Value)
 -> LspM c (Either PluginError Value))
-> IO (Either PluginError Value)
-> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ do
    Age
count <- ShakeDatabase -> IO Age
shakeGetBuildEdges (ShakeDatabase -> IO Age) -> ShakeDatabase -> IO Age
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeDatabase
shakeDb IdeState
s
    Either PluginError Value -> IO (Either PluginError Value)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> IO (Either PluginError Value))
-> Either PluginError Value -> IO (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ Age -> Value
forall a. ToJSON a => a -> Value
toJSON Age
count
testRequestHandler IdeState
s (GarbageCollectDirtyKeys CheckParents
parents Age
age) = do
    [Key]
res <- IO [Key] -> LspT c IO [Key]
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [Key] -> LspT c IO [Key]) -> IO [Key] -> LspT c IO [Key]
forall a b. (a -> b) -> a -> b
$ String -> IdeState -> Action [Key] -> IO [Key]
forall a. String -> IdeState -> Action a -> IO a
runAction String
"garbage collect dirty" IdeState
s (Action [Key] -> IO [Key]) -> Action [Key] -> IO [Key]
forall a b. (a -> b) -> a -> b
$ Age -> CheckParents -> Action [Key]
garbageCollectDirtyKeysOlderThan Age
age CheckParents
parents
    Either PluginError Value -> LspM c (Either PluginError Value)
forall a. a -> LspT c IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> LspM c (Either PluginError Value))
-> Either PluginError Value -> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (Key -> String) -> [Key] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Key -> String
forall a. Show a => a -> String
show [Key]
res
testRequestHandler IdeState
s TestRequest
GetStoredKeys = do
    [Key]
keys <- IO [Key] -> LspT c IO [Key]
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [Key] -> LspT c IO [Key]) -> IO [Key] -> LspT c IO [Key]
forall a b. (a -> b) -> a -> b
$ STM [Key] -> IO [Key]
forall a. STM a -> IO a
atomically (STM [Key] -> IO [Key]) -> STM [Key] -> IO [Key]
forall a b. (a -> b) -> a -> b
$ ((Key, ValueWithDiagnostics) -> Key)
-> [(Key, ValueWithDiagnostics)] -> [Key]
forall a b. (a -> b) -> [a] -> [b]
map (Key, ValueWithDiagnostics) -> Key
forall a b. (a, b) -> a
fst ([(Key, ValueWithDiagnostics)] -> [Key])
-> STM [(Key, ValueWithDiagnostics)] -> STM [Key]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ListT STM (Key, ValueWithDiagnostics)
-> STM [(Key, ValueWithDiagnostics)]
forall (m :: * -> *) a. Monad m => ListT m a -> m [a]
ListT.toList (Map Key ValueWithDiagnostics
-> ListT STM (Key, ValueWithDiagnostics)
forall key value. Map key value -> ListT STM (key, value)
STM.listT (Map Key ValueWithDiagnostics
 -> ListT STM (Key, ValueWithDiagnostics))
-> Map Key ValueWithDiagnostics
-> ListT STM (Key, ValueWithDiagnostics)
forall a b. (a -> b) -> a -> b
$ ShakeExtras -> Map Key ValueWithDiagnostics
state (ShakeExtras -> Map Key ValueWithDiagnostics)
-> ShakeExtras -> Map Key ValueWithDiagnostics
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeExtras
shakeExtras IdeState
s)
    Either PluginError Value -> LspM c (Either PluginError Value)
forall a. a -> LspT c IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> LspM c (Either PluginError Value))
-> Either PluginError Value -> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (Key -> String) -> [Key] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Key -> String
forall a. Show a => a -> String
show [Key]
keys
testRequestHandler IdeState
s TestRequest
GetFilesOfInterest = do
    HashMap NormalizedFilePath FileOfInterestStatus
ff <- IO (HashMap NormalizedFilePath FileOfInterestStatus)
-> LspT c IO (HashMap NormalizedFilePath FileOfInterestStatus)
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (HashMap NormalizedFilePath FileOfInterestStatus)
 -> LspT c IO (HashMap NormalizedFilePath FileOfInterestStatus))
-> IO (HashMap NormalizedFilePath FileOfInterestStatus)
-> LspT c IO (HashMap NormalizedFilePath FileOfInterestStatus)
forall a b. (a -> b) -> a -> b
$ IdeState -> IO (HashMap NormalizedFilePath FileOfInterestStatus)
getFilesOfInterest IdeState
s
    Either PluginError Value -> LspM c (Either PluginError Value)
forall a. a -> LspT c IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> LspM c (Either PluginError Value))
-> Either PluginError Value -> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ [String] -> Value
forall a. ToJSON a => a -> Value
toJSON ([String] -> Value) -> [String] -> Value
forall a b. (a -> b) -> a -> b
$ (NormalizedFilePath -> String) -> [NormalizedFilePath] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map NormalizedFilePath -> String
fromNormalizedFilePath ([NormalizedFilePath] -> [String])
-> [NormalizedFilePath] -> [String]
forall a b. (a -> b) -> a -> b
$ HashMap NormalizedFilePath FileOfInterestStatus
-> [NormalizedFilePath]
forall k v. HashMap k v -> [k]
HM.keys HashMap NormalizedFilePath FileOfInterestStatus
ff
testRequestHandler IdeState
s TestRequest
GetRebuildsCount = do
    Age
count <- IO Age -> LspT c IO Age
forall a. IO a -> LspT c IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Age -> LspT c IO Age) -> IO Age -> LspT c IO Age
forall a b. (a -> b) -> a -> b
$ String -> IdeState -> Action Age -> IO Age
forall a. String -> IdeState -> Action a -> IO a
runAction String
"get build count" IdeState
s Action Age
getRebuildCount
    Either PluginError Value -> LspM c (Either PluginError Value)
forall a. a -> LspT c IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PluginError Value -> LspM c (Either PluginError Value))
-> Either PluginError Value -> LspM c (Either PluginError Value)
forall a b. (a -> b) -> a -> b
$ Value -> Either PluginError Value
forall a b. b -> Either a b
Right (Value -> Either PluginError Value)
-> Value -> Either PluginError Value
forall a b. (a -> b) -> a -> b
$ Age -> Value
forall a. ToJSON a => a -> Value
toJSON Age
count

getDatabaseKeys :: (Graph.Result -> Step)
    -> ShakeDatabase
    -> IO [Graph.Key]
getDatabaseKeys :: (Result -> Step) -> ShakeDatabase -> IO [Key]
getDatabaseKeys Result -> Step
field ShakeDatabase
db = do
    [(Key, Result)]
keys <- ShakeDatabase -> IO [(Key, Result)]
shakeGetCleanKeys ShakeDatabase
db
    Age
step <- ShakeDatabase -> IO Age
shakeGetBuildStep ShakeDatabase
db
    [Key] -> IO [Key]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [ Key
k | (Key
k, Result
res) <- [(Key, Result)]
keys, Result -> Step
field Result
res Step -> Step -> Bool
forall a. Eq a => a -> a -> Bool
== Age -> Step
Step Age
step]

parseAction :: CI String -> NormalizedFilePath -> Action (Either Text Bool)
parseAction :: CI String -> NormalizedFilePath -> Action (Either Text Bool)
parseAction CI String
"typecheck" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe TcModuleResult -> Bool)
-> Maybe TcModuleResult
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe TcModuleResult -> Bool
forall a. Maybe a -> Bool
isJust (Maybe TcModuleResult -> Either Text Bool)
-> Action (Maybe TcModuleResult) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TypeCheck -> NormalizedFilePath -> Action (Maybe TcModuleResult)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use TypeCheck
TypeCheck NormalizedFilePath
fp
parseAction CI String
"getLocatedImports" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe [(Located ModuleName, Maybe ArtifactsLocation)] -> Bool)
-> Maybe [(Located ModuleName, Maybe ArtifactsLocation)]
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe [(Located ModuleName, Maybe ArtifactsLocation)] -> Bool
forall a. Maybe a -> Bool
isJust (Maybe [(Located ModuleName, Maybe ArtifactsLocation)]
 -> Either Text Bool)
-> Action (Maybe [(Located ModuleName, Maybe ArtifactsLocation)])
-> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetLocatedImports
-> NormalizedFilePath
-> Action (Maybe [(Located ModuleName, Maybe ArtifactsLocation)])
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetLocatedImports
GetLocatedImports NormalizedFilePath
fp
parseAction CI String
"getmodsummary" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe ModSummaryResult -> Bool)
-> Maybe ModSummaryResult
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe ModSummaryResult -> Bool
forall a. Maybe a -> Bool
isJust (Maybe ModSummaryResult -> Either Text Bool)
-> Action (Maybe ModSummaryResult) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetModSummary
-> NormalizedFilePath -> Action (Maybe ModSummaryResult)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetModSummary
GetModSummary NormalizedFilePath
fp
parseAction CI String
"getmodsummarywithouttimestamps" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe ModSummaryResult -> Bool)
-> Maybe ModSummaryResult
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe ModSummaryResult -> Bool
forall a. Maybe a -> Bool
isJust (Maybe ModSummaryResult -> Either Text Bool)
-> Action (Maybe ModSummaryResult) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetModSummaryWithoutTimestamps
-> NormalizedFilePath -> Action (Maybe ModSummaryResult)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetModSummaryWithoutTimestamps
GetModSummaryWithoutTimestamps NormalizedFilePath
fp
parseAction CI String
"getparsedmodule" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe ParsedModule -> Bool)
-> Maybe ParsedModule
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe ParsedModule -> Bool
forall a. Maybe a -> Bool
isJust (Maybe ParsedModule -> Either Text Bool)
-> Action (Maybe ParsedModule) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetParsedModule
-> NormalizedFilePath -> Action (Maybe ParsedModule)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetParsedModule
GetParsedModule NormalizedFilePath
fp
parseAction CI String
"ghcsession" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe HscEnvEq -> Bool) -> Maybe HscEnvEq -> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe HscEnvEq -> Bool
forall a. Maybe a -> Bool
isJust (Maybe HscEnvEq -> Either Text Bool)
-> Action (Maybe HscEnvEq) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GhcSession -> NormalizedFilePath -> Action (Maybe HscEnvEq)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GhcSession
GhcSession NormalizedFilePath
fp
parseAction CI String
"ghcsessiondeps" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe HscEnvEq -> Bool) -> Maybe HscEnvEq -> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe HscEnvEq -> Bool
forall a. Maybe a -> Bool
isJust (Maybe HscEnvEq -> Either Text Bool)
-> Action (Maybe HscEnvEq) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GhcSessionDeps -> NormalizedFilePath -> Action (Maybe HscEnvEq)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GhcSessionDeps
GhcSessionDeps NormalizedFilePath
fp
parseAction CI String
"gethieast" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe HieAstResult -> Bool)
-> Maybe HieAstResult
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe HieAstResult -> Bool
forall a. Maybe a -> Bool
isJust (Maybe HieAstResult -> Either Text Bool)
-> Action (Maybe HieAstResult) -> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetHieAst -> NormalizedFilePath -> Action (Maybe HieAstResult)
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetHieAst
GetHieAst NormalizedFilePath
fp
parseAction CI String
"getFileContents" NormalizedFilePath
fp = Bool -> Either Text Bool
forall a b. b -> Either a b
Right (Bool -> Either Text Bool)
-> (Maybe (FileVersion, Maybe Text) -> Bool)
-> Maybe (FileVersion, Maybe Text)
-> Either Text Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe (FileVersion, Maybe Text) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (FileVersion, Maybe Text) -> Either Text Bool)
-> Action (Maybe (FileVersion, Maybe Text))
-> Action (Either Text Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GetFileContents
-> NormalizedFilePath -> Action (Maybe (FileVersion, Maybe Text))
forall k v.
IdeRule k v =>
k -> NormalizedFilePath -> Action (Maybe v)
use GetFileContents
GetFileContents NormalizedFilePath
fp
parseAction CI String
other NormalizedFilePath
_ = Either Text Bool -> Action (Either Text Bool)
forall a. a -> Action a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Text Bool -> Action (Either Text Bool))
-> Either Text Bool -> Action (Either Text Bool)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text Bool
forall a b. a -> Either a b
Left (Text -> Either Text Bool) -> Text -> Either Text Bool
forall a b. (a -> b) -> a -> b
$ Text
"Cannot parse ide rule: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (CI String -> String
forall s. CI s -> s
original CI String
other)

-- | a command that blocks forever. Used for testing
blockCommandId :: Text
blockCommandId :: Text
blockCommandId = Text
"ghcide.command.block"

blockCommandDescriptor :: PluginId -> PluginDescriptor state
blockCommandDescriptor :: forall state. PluginId -> PluginDescriptor state
blockCommandDescriptor PluginId
plId = (PluginId -> Text -> PluginDescriptor state
forall ideState. PluginId -> Text -> PluginDescriptor ideState
defaultPluginDescriptor PluginId
plId Text
"") {
    pluginCommands = [PluginCommand (CommandId blockCommandId) "blocks forever" blockCommandHandler]
}

blockCommandHandler :: CommandFunction state ExecuteCommandParams
blockCommandHandler :: forall state. CommandFunction state ExecuteCommandParams
blockCommandHandler state
_ideState Maybe ProgressToken
_ ExecuteCommandParams
_params = do
  LspM Config () -> ExceptT PluginError (LspT Config IO) ()
forall (m :: * -> *) a. Monad m => m a -> ExceptT PluginError m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (LspM Config () -> ExceptT PluginError (LspT Config IO) ())
-> LspM Config () -> ExceptT PluginError (LspT Config IO) ()
forall a b. (a -> b) -> a -> b
$ SServerMethod ('Method_CustomMethod "ghcide/blocking/command")
-> MessageParams ('Method_CustomMethod "ghcide/blocking/command")
-> LspM Config ()
forall (m :: Method 'ServerToClient 'Notification) (f :: * -> *)
       config.
MonadLsp config f =>
SServerMethod m -> MessageParams m -> f ()
LSP.sendNotification (Proxy "ghcide/blocking/command"
-> SServerMethod ('Method_CustomMethod "ghcide/blocking/command")
forall {f :: MessageDirection} {t :: MessageKind} (s :: Symbol).
KnownSymbol s =>
Proxy s -> SMethod ('Method_CustomMethod s)
SMethod_CustomMethod (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @"ghcide/blocking/command")) Value
MessageParams ('Method_CustomMethod "ghcide/blocking/command")
A.Null
  IO () -> ExceptT PluginError (LspT Config IO) ()
forall a. IO a -> ExceptT PluginError (LspT Config IO) a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ExceptT PluginError (LspT Config IO) ())
-> IO () -> ExceptT PluginError (LspT Config IO) ()
forall a b. (a -> b) -> a -> b
$ Age -> IO ()
threadDelay Age
forall a. Bounded a => a
maxBound
  (Value |? Null)
-> ExceptT PluginError (LspT Config IO) (Value |? Null)
forall a. a -> ExceptT PluginError (LspT Config IO) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((Value |? Null)
 -> ExceptT PluginError (LspT Config IO) (Value |? Null))
-> (Value |? Null)
-> ExceptT PluginError (LspT Config IO) (Value |? Null)
forall a b. (a -> b) -> a -> b
$ Null -> Value |? Null
forall a b. b -> a |? b
InR Null
Null