{-# LANGUAGE GADTs #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Haskell.Debug.Adapter.State.DebugRun where
import Control.Monad.IO.Class
import qualified System.Log.Logger as L
import Control.Monad.State.Lazy
import Control.Monad.Except
import Control.Lens
import qualified Text.Read as R
import qualified Haskell.DAP as DAP
import Haskell.Debug.Adapter.Constant
import qualified Haskell.Debug.Adapter.Utility as U
import Haskell.Debug.Adapter.Type
import qualified Haskell.Debug.Adapter.GHCi as P
import Haskell.Debug.Adapter.State.DebugRun.Threads()
import Haskell.Debug.Adapter.State.DebugRun.StackTrace()
import Haskell.Debug.Adapter.State.DebugRun.Scopes()
import Haskell.Debug.Adapter.State.DebugRun.Variables()
import Haskell.Debug.Adapter.State.DebugRun.Continue()
import Haskell.Debug.Adapter.State.DebugRun.Next()
import Haskell.Debug.Adapter.State.DebugRun.StepIn()
import Haskell.Debug.Adapter.State.DebugRun.Terminate()
import Haskell.Debug.Adapter.State.DebugRun.InternalTerminate()
import qualified Haskell.Debug.Adapter.State.Utility as SU
instance AppStateIF DebugRunStateData where
entryAction DebugRunState = do
liftIO $ L.debugM _LOG_APP "DebugRunState entryAction called."
goEntry
exitAction DebugRunState = do
liftIO $ L.debugM _LOG_APP "DebugRunState exitAction called."
return ()
doActivity s (WrapRequest r@InitializeRequest{}) = action s r
doActivity s (WrapRequest r@LaunchRequest{}) = action s r
doActivity s (WrapRequest r@DisconnectRequest{}) = action s r
doActivity s (WrapRequest r@PauseRequest{}) = action s r
doActivity s (WrapRequest r@TerminateRequest{}) = action s r
doActivity s (WrapRequest r@SetBreakpointsRequest{}) = action s r
doActivity s (WrapRequest r@SetFunctionBreakpointsRequest{}) = action s r
doActivity s (WrapRequest r@SetExceptionBreakpointsRequest{}) = action s r
doActivity s (WrapRequest r@ConfigurationDoneRequest{}) = action s r
doActivity s (WrapRequest r@ThreadsRequest{}) = action s r
doActivity s (WrapRequest r@StackTraceRequest{}) = action s r
doActivity s (WrapRequest r@ScopesRequest{}) = action s r
doActivity s (WrapRequest r@VariablesRequest{}) = action s r
doActivity s (WrapRequest r@ContinueRequest{}) = action s r
doActivity s (WrapRequest r@NextRequest{}) = action s r
doActivity s (WrapRequest r@StepInRequest{}) = action s r
doActivity s (WrapRequest r@EvaluateRequest{}) = action s r
doActivity s (WrapRequest r@CompletionsRequest{}) = action s r
doActivity s (WrapRequest r@InternalTransitRequest{}) = action s r
doActivity s (WrapRequest r@InternalTerminateRequest{}) = action s r
doActivity s (WrapRequest r@InternalLoadRequest{}) = action s r
instance StateActivityIF DebugRunStateData DAP.InitializeRequest
instance StateActivityIF DebugRunStateData DAP.LaunchRequest
instance StateActivityIF DebugRunStateData DAP.DisconnectRequest
instance StateActivityIF DebugRunStateData DAP.PauseRequest
goEntry :: AppContext ()
goEntry = view stopOnEntryAppStores <$> get >>= \case
True -> stopOnEntry
False -> startDebug
stopOnEntry :: AppContext ()
stopOnEntry = do
startupFile <- view startupAppStores <$> get
startupFunc <- view startupFuncAppStores <$> get
let funcName = if null startupFunc then "main" else startupFunc
funcBp = (startupFile, DAP.FunctionBreakpoint funcName Nothing Nothing)
cmd = ":dap-set-function-breakpoint " ++ U.showDAP funcBp
P.command cmd
res <- P.expectPmpt
withAdhocAddDapHeader $ filter (U.startswith _DAP_HEADER) res
where
withAdhocAddDapHeader :: [String] -> AppContext ()
withAdhocAddDapHeader [] = do
U.warnEV _LOG_APP $ "can not set func breakpoint. no dap header found."
startDebug
withAdhocAddDapHeader (str:[]) = case R.readEither (drop (length _DAP_HEADER) str) of
Left err -> do
U.warnEV _LOG_APP $ "read response body failed. " ++ err ++ " : " ++ str
startDebug
Right (Left err) -> do
U.warnEV _LOG_APP $ "set adhoc breakpoint failed. " ++ err ++ " : " ++ str
startDebug
Right (Right bp) -> do
startDebug
adhocDelBreakpoint bp
withAdhocAddDapHeader _ = do
U.warnEV _LOG_APP $ "can not set func breakpoint. ambiguous dap header found."
startDebug
adhocDelBreakpoint :: DAP.Breakpoint -> AppContext ()
adhocDelBreakpoint bp = do
let cmd = ":dap-delete-breakpoint " ++ U.showDAP bp
P.command cmd
res <- P.expectPmpt
withAdhocDelDapHeader $ filter (U.startswith _DAP_HEADER) res
withAdhocDelDapHeader :: [String] -> AppContext ()
withAdhocDelDapHeader [] = throwError $ "can not del func breakpoint. no dap header found."
withAdhocDelDapHeader (str:[]) = case R.readEither (drop (length _DAP_HEADER) str) of
Left err -> throwError $ "read response body failed. " ++ err ++ " : " ++ str
Right (Left err) -> throwError $ "del adhoc breakpoint failed. " ++ err ++ " : " ++ str
Right (Right res) -> return res
withAdhocDelDapHeader _ = throwError $ "can not del func breakpoint. ambiguous dap header found."
startDebug :: AppContext ()
startDebug = do
expr <- getTraceExpr
let args = DAP.defaultContinueRequestArguments {
DAP.exprContinueRequestArguments = Just expr
}
startDebugDAP args
where
getTraceExpr = view startupFuncAppStores <$> get >>= \case
[] -> return "main"
func -> do
funcArgs <- view startupArgsAppStores <$> get
return $ U.strip $ func ++ " " ++ funcArgs
startDebugDAP args = do
let dap = ":dap-continue "
cmd = dap ++ U.showDAP args
dbg = dap ++ show args
P.command cmd
U.debugEV _LOG_APP dbg
P.expectPmpt >>= SU.takeDapResult >>= dapHdl
return ()
dapHdl :: String -> AppContext ()
dapHdl str = case R.readEither str of
Left err -> errHdl str err
Right (Left err) -> errHdl str err
Right (Right body) -> U.handleStoppedEventBody body
errHdl :: String -> String -> AppContext()
errHdl str err = do
let msg = "start debugging failed. " ++ err ++ " : " ++ str
liftIO $ L.errorM _LOG_APP msg
U.sendErrorEventLF msg
instance StateActivityIF DebugRunStateData DAP.SetBreakpointsRequest where
action _ (SetBreakpointsRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState SetBreakpointsRequest called. " ++ show req
SU.setBreakpointsRequest req
instance StateActivityIF DebugRunStateData DAP.SetExceptionBreakpointsRequest where
action _ (SetExceptionBreakpointsRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState SetExceptionBreakpointsRequest called. " ++ show req
SU.setExceptionBreakpointsRequest req
instance StateActivityIF DebugRunStateData DAP.SetFunctionBreakpointsRequest where
action _ (SetFunctionBreakpointsRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState SetFunctionBreakpointsRequest called. " ++ show req
SU.setFunctionBreakpointsRequest req
instance StateActivityIF DebugRunStateData DAP.ConfigurationDoneRequest
instance StateActivityIF DebugRunStateData DAP.EvaluateRequest where
action _ (EvaluateRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState EvaluateRequest called. " ++ show req
SU.evaluateRequest req
instance StateActivityIF DebugRunStateData DAP.CompletionsRequest where
action _ (CompletionsRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState CompletionsRequest called. " ++ show req
SU.completionsRequest req
instance StateActivityIF DebugRunStateData HdaInternalTransitRequest
instance StateActivityIF DebugRunStateData HdaInternalLoadRequest where
action _ (InternalLoadRequest req) = do
liftIO $ L.debugM _LOG_APP $ "DebugRunState InternalLoadRequest called. " ++ show req
SU.loadHsFile $ pathHdaInternalLoadRequest req
return $ Just DebugRun_Contaminated