haskell-debugger-0.5.0.0: hdb/Development/Debug/Adapter/Evaluation.hs
{-# LANGUAGE RecordWildCards, OverloadedRecordDot, DuplicateRecordFields #-}
module Development.Debug.Adapter.Evaluation where
import qualified Data.Text as T
import qualified Data.Map as M
import qualified Data.IntSet as IS
import DAP
import GHC.Debugger.Interface.Messages
import Development.Debug.Adapter
import Development.Debug.Adapter.Interface
import qualified Development.Debug.Adapter.Output as Output
--------------------------------------------------------------------------------
-- * Executing debuggee
--------------------------------------------------------------------------------
-- | Start executing from entry point
--
-- TODO:
-- [ ] Consider using Output events for debuggee evaluation.
startExecution :: DebugAdaptor EvalResult
startExecution = do
DAS{entryPoint, entryArgs} <- getDebugSession
let entry
| entryPoint == "main" = MainEntry Nothing
| otherwise = FunctionEntry entryPoint
DidExec er <- sendSync DebugExecution{entryPoint = entry, runArgs = entryArgs}
return er
--------------------------------------------------------------------------------
-- * Eval
--------------------------------------------------------------------------------
-- | Command for evaluation (includes evaluation-on-hover)
commandEvaluate :: DebugAdaptor ()
commandEvaluate = do
EvaluateArguments {..} <- getArguments
DidEval er <- sendSync (DoEval (T.unpack evaluateArgumentsExpression))
case er of
EvalStopped{} -> error "impossible, execution is resumed automatically for 'DoEval'"
EvalAbortedWith e -> do
-- Evaluation failed, we report it but don't terminate.
sendEvaluateResponse EvaluateResponse
{ evaluateResponseResult = T.pack e
, evaluateResponseType = T.pack ""
, evaluateResponsePresentationHint = Nothing
, evaluateResponseVariablesReference = 0
, evaluateResponseNamedVariables = Nothing
, evaluateResponseIndexedVariables = Nothing
, evaluateResponseMemoryReference = Nothing
}
_ -> do
sendEvaluateResponse EvaluateResponse
{ evaluateResponseResult = T.pack $ resultVal er
, evaluateResponseType = T.pack $ resultType er
, evaluateResponsePresentationHint = Nothing
, evaluateResponseVariablesReference = 0
, evaluateResponseNamedVariables = Nothing
, evaluateResponseIndexedVariables = Nothing
, evaluateResponseMemoryReference = Nothing
}
--------------------------------------------------------------------------------
-- * Utils
--------------------------------------------------------------------------------
-- | Handle an EvalResult by sending a stopped or exited event.
--
-- In particular, the result of evaluation is ignored by this function.
-- The 'EvaluateRequest' handler inspects the EvalResult itself and reports on the result.
handleEvalResult :: Bool {-^ Whether we are "stepping" -} -> EvalResult -> DebugAdaptor ()
handleEvalResult stepping er = case er of
EvalAbortedWith e -> do
Output.console (T.pack e)
sendTerminatedEvent defaultTerminatedEvent
sendExitedEvent (ExitedEvent 43)
EvalCompleted{} -> do
sendTerminatedEvent defaultTerminatedEvent
sendExitedEvent (ExitedEvent 0)
EvalException{} -> do
sendTerminatedEvent defaultTerminatedEvent
sendExitedEvent (ExitedEvent 42)
EvalStopped {breakId = Nothing} ->
sendStoppedEvent
defaultStoppedEvent {
stoppedEventAllThreadsStopped = True
, stoppedEventReason = StoppedEventReasonException
, stoppedEventHitBreakpointIds = []
}
EvalStopped {breakId = Just bid} -> do
DAS{breakpointMap} <- getDebugSession
sendStoppedEvent
defaultStoppedEvent {
stoppedEventAllThreadsStopped = True
-- could be more precise here by saying "function breakpoint" rather than always "breakpoint"
, stoppedEventReason
= if stepping then StoppedEventReasonStep
else StoppedEventReasonBreakpoint
, stoppedEventHitBreakpointIds
= maybe [] IS.toList (M.lookup bid breakpointMap)
}