@@ -192,11 +192,27 @@ object Parse {
192192 def sourceChanges (): Stream [IO , DAPodil .Source ] =
193193 Stream .empty
194194
195- def step (): IO [Unit ] =
196- control.step () *> parseEvents
195+ def stepOver (): IO [Unit ] =
196+ control.stepOver () *> parseEvents
197197 .send(
198198 Parse .Event
199- .Control (DAPodil .Debugee .State .Stopped .step)
199+ .Control (DAPodil .Debugee .State .Stopped .stepOver)
200+ )
201+ .void
202+
203+ def stepIn (): IO [Unit ] =
204+ control.stepIn() *> parseEvents
205+ .send(
206+ Parse .Event
207+ .Control (DAPodil .Debugee .State .Stopped .stepIn)
208+ )
209+ .void
210+
211+ def stepOut (): IO [Unit ] =
212+ control.stepOut() *> parseEvents
213+ .send(
214+ Parse .Event
215+ .Control (DAPodil .Debugee .State .Stopped .stepOut)
200216 )
201217 .void
202218
@@ -748,7 +764,7 @@ object Parse {
748764
749765 startup = dapEvents.send(ConfigEvent (args)) *>
750766 (if (args.stopOnEntry)
751- control.step () *>
767+ control.stepIn () *>
752768 events.send(Parse .Event .Control (DAPodil .Debugee .State .Stopped .entry))
753769 // don't use debugee.step as we need to send Stopped.entry, not Stopped.step
754770 else debugee.continue())
@@ -818,8 +834,12 @@ object Parse {
818834 new Events .StoppedEvent (" entry" , 1L )
819835 case DAPodil .Debugee .State .Stopped .Reason .Pause =>
820836 new Events .StoppedEvent (" pause" , 1L )
821- case DAPodil .Debugee .State .Stopped .Reason .Step =>
837+ case DAPodil .Debugee .State .Stopped .Reason .StepOver =>
822838 new Events .StoppedEvent (" step" , 1L )
839+ case DAPodil .Debugee .State .Stopped .Reason .StepIn =>
840+ new Events .StoppedEvent (" stepIn" , 1L )
841+ case DAPodil .Debugee .State .Stopped .Reason .StepOut =>
842+ new Events .StoppedEvent (" stepOut" , 1L )
823843 case DAPodil .Debugee .State .Stopped .Reason .BreakpointHit (location) =>
824844 new Events .StoppedEvent (" breakpoint" , 1L , false , show " Breakpoint hit at $location" , null )
825845 },
@@ -1248,6 +1268,16 @@ object Parse {
12481268 */
12491269 def await (): IO [Boolean ]
12501270
1271+ /** Update the control with the current element depth (set by the parser thread before awaiting). Depth is used to
1272+ * implement step/stepOut semantics.
1273+ */
1274+ def setCurrentDepth (depth : Int ): IO [Unit ]
1275+
1276+ /** Indicate the kind of the upcoming await (e.g. "start" or "end"). Parser hooks should set this before calling
1277+ * await().
1278+ */
1279+ def setAwaitingKind (kind : String ): IO [Unit ]
1280+
12511281 /** Start running. */
12521282 def continue (): IO [Unit ]
12531283
@@ -1256,7 +1286,9 @@ object Parse {
12561286 * IMPORTANT: Shouldn't return until any work blocked by an `await` completes, otherwise the update that was
12571287 * waiting will race with the code that sees the `step` complete.
12581288 */
1259- def step (): IO [Unit ]
1289+ def stepOver (): IO [Unit ]
1290+ def stepIn (): IO [Unit ]
1291+ def stepOut (): IO [Unit ]
12601292
12611293 /** Stop running. */
12621294 def pause (): IO [Unit ]
@@ -1275,6 +1307,9 @@ object Parse {
12751307 for {
12761308 waiterArrived <- Deferred [IO , Unit ]
12771309 state <- Ref [IO ].of[State ](AwaitingFirstAwait (waiterArrived))
1310+ currentDepth <- Ref [IO ].of[Int ](0 )
1311+ awaitingKind <- Ref [IO ].of[String ](" " )
1312+ stopTarget <- Ref [IO ].of[Option [(Int , String )]](None )
12781313 } yield new Control {
12791314 def await (): IO [Boolean ] =
12801315 for {
@@ -1286,34 +1321,75 @@ object Parse {
12861321 .complete(()) *> nextContinue.get.as(true )
12871322 case Running => Running -> IO .pure(false )
12881323 case s @ Stopped (whenContinued, nextAwaitStarted) =>
1289- s -> nextAwaitStarted.complete(()) *> // signal next await happened
1290- whenContinued.get.as(true ) // block
1324+ s -> nextAwaitStarted.complete(()) *>
1325+ // Decide whether to block or let parser continue
1326+ stopTarget.get.flatMap {
1327+ case None => whenContinued.get.as(true )
1328+ case Some ((targetDepth, mode)) =>
1329+ for {
1330+ cur <- currentDepth.get
1331+ kind <- awaitingKind.get
1332+ res <- mode match {
1333+ // Allow running while deeper than target.
1334+ // When at or above target and we're at an end-element, block once then clear target
1335+ case " stepOut" =>
1336+ if (cur > targetDepth) IO .pure(false )
1337+ else if (kind == " end" ) whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1338+ else IO .pure(false )
1339+
1340+ // Allow running until we reach a deeper depth, then pause at the start-element and clear target
1341+ case " stepIn" =>
1342+ if (cur < targetDepth) IO .pure(false )
1343+ else if (kind == " start" ) whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1344+ else IO .pure(false )
1345+
1346+ // Block once and clear the stopTarget so subsequent awaits don't re-trigger
1347+ case _ => whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1348+ }
1349+ } yield res
1350+ }
12911351 }.flatten
12921352 } yield awaited
12931353
1294- def step ( ): IO [Unit ] =
1354+ def performStep ( stepType : String , addedDepth : Int ): IO [Unit ] =
12951355 for {
12961356 nextContinue <- Deferred [IO , Unit ]
12971357 nextAwaitStarted <- Deferred [IO , Unit ]
12981358 _ <- state.modify {
12991359 case s @ AwaitingFirstAwait (waiterArrived) =>
1300- s -> waiterArrived.get *> step()
1360+ s -> waiterArrived.get *> (stepType match {
1361+ case " stepOver" => stepOver()
1362+ case " stepIn" => stepIn()
1363+ case " stepOut" => stepOut()
1364+ })
13011365 case Running => Running -> IO .unit
13021366 case Stopped (whenContinued, _) =>
13031367 Stopped (nextContinue, nextAwaitStarted) -> (
1304- whenContinued.complete(()) *> // wake up await-ers
1305- nextAwaitStarted.get // block until next await is invoked
1368+ for {
1369+ d <- currentDepth.get
1370+ _ <- stopTarget.set(Some ((d + addedDepth, stepType)))
1371+ _ <- whenContinued.complete(())
1372+ _ <- nextAwaitStarted.get
1373+ } yield ()
13061374 )
13071375 }.flatten
13081376 } yield ()
13091377
1378+ def stepOver (): IO [Unit ] = performStep(" stepOver" , 0 )
1379+ def stepIn (): IO [Unit ] = performStep(" stepIn" , 1 )
1380+ def stepOut (): IO [Unit ] = performStep(" stepOut" , - 1 )
1381+
1382+ def setCurrentDepth (depth : Int ): IO [Unit ] = currentDepth.set(depth)
1383+
1384+ def setAwaitingKind (kind : String ): IO [Unit ] = awaitingKind.set(kind)
1385+
13101386 def continue (): IO [Unit ] =
13111387 state.modify {
13121388 case s @ AwaitingFirstAwait (waiterArrived) =>
13131389 s -> waiterArrived.get *> continue()
1314- case Running => Running -> IO .unit
1390+ case Running => Running -> IO .unit
13151391 case Stopped (whenContinued, _) =>
1316- Running -> whenContinued.complete(()).void // wake up await-ers
1392+ Running -> (stopTarget.set( None ) *> whenContinued.complete(() )).void // wake up await-ers
13171393 }.flatten
13181394
13191395 def pause (): IO [Unit ] =
@@ -1370,7 +1446,21 @@ object Parse {
13701446
13711447 for {
13721448 _ <- logger.debug(" pre-control await" )
1373- isStepping <- control.await() // may block until external control says to unblock, for stepping behavior
1449+ _ <- control.setCurrentDepth(
1450+ pstate.currentNode.toOption
1451+ .map { node =>
1452+ var depth = 0
1453+ var n = node
1454+ while (n.diParent != null ) {
1455+ depth += 1
1456+ n = n.diParent
1457+ }
1458+ depth
1459+ }
1460+ .getOrElse(0 )
1461+ )
1462+ _ <- control.setAwaitingKind(" start" )
1463+ isStepping <- control.await()
13741464 _ <- logger.debug(" post-control await" )
13751465 location = createLocation(pstate.schemaFileLocation)
13761466 shouldBreak <- breakpoints.shouldBreak(location)
@@ -1403,7 +1493,21 @@ object Parse {
14031493
14041494 override def endElement (pstate : PState , processor : Parser ): Unit =
14051495 dispatcher.unsafeRunSync {
1406- control.await() *> // ensure no events while debugger is paused
1496+ val depth = pstate.currentNode.toOption
1497+ .map { node =>
1498+ var d = 0
1499+ var n = node
1500+ while (n.diParent != null ) {
1501+ d += 1
1502+ n = n.diParent
1503+ }
1504+ d
1505+ }
1506+ .getOrElse(0 )
1507+
1508+ control.setCurrentDepth(depth) *>
1509+ control.setAwaitingKind(" end" ) *>
1510+ control.await() *>
14071511 events.send(Event .EndElement (pstate.copyStateForDebugger)).void
14081512 }
14091513 }
0 commit comments