@@ -1251,6 +1251,16 @@ object Parse {
12511251 */
12521252 def await (): IO [Boolean ]
12531253
1254+ /** Update the control with the current element depth (set by the parser thread before awaiting). Depth is used to
1255+ * implement step/stepOut semantics.
1256+ */
1257+ def setCurrentDepth (depth : Int ): IO [Unit ]
1258+
1259+ /** Indicate the kind of the upcoming await (e.g. "start" or "end"). Parser hooks should set this before calling
1260+ * await().
1261+ */
1262+ def setAwaitingKind (kind : String ): IO [Unit ]
1263+
12541264 /** Start running. */
12551265 def continue (): IO [Unit ]
12561266
@@ -1280,6 +1290,9 @@ object Parse {
12801290 for {
12811291 waiterArrived <- Deferred [IO , Unit ]
12821292 state <- Ref [IO ].of[State ](AwaitingFirstAwait (waiterArrived))
1293+ currentDepth <- Ref [IO ].of[Int ](0 )
1294+ awaitingKind <- Ref [IO ].of[String ](" " )
1295+ stopTarget <- Ref [IO ].of[Option [(Int , String )]](None )
12831296 } yield new Control {
12841297 def await (): IO [Boolean ] =
12851298 for {
@@ -1291,12 +1304,37 @@ object Parse {
12911304 .complete(()) *> nextContinue.get.as(true )
12921305 case Running => Running -> IO .pure(false )
12931306 case s @ Stopped (whenContinued, nextAwaitStarted) =>
1294- s -> nextAwaitStarted.complete(()) *> // signal next await happened
1295- whenContinued.get.as(true ) // block
1307+ s -> nextAwaitStarted.complete(()) *>
1308+ // Decide whether to block or let parser continue
1309+ stopTarget.get.flatMap {
1310+ case None => whenContinued.get.as(true )
1311+ case Some ((targetDepth, mode)) =>
1312+ for {
1313+ cur <- currentDepth.get
1314+ kind <- awaitingKind.get
1315+ res <- mode match {
1316+ // Allow running while deeper than target.
1317+ // When at or above target and we're at an end-element, block once then clear target
1318+ case " stepOut" =>
1319+ if (cur > targetDepth) IO .pure(false )
1320+ else if (kind == " end" ) whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1321+ else IO .pure(false )
1322+
1323+ // Allow running until we reach a deeper depth, then pause at the start-element and clear target
1324+ case " stepIn" =>
1325+ if (cur < targetDepth) IO .pure(false )
1326+ else if (kind == " start" ) whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1327+ else IO .pure(false )
1328+
1329+ // Block once and clear the stopTarget so subsequent awaits don't re-trigger
1330+ case _ => whenContinued.get.flatMap(_ => stopTarget.set(None ).as(true ))
1331+ }
1332+ } yield res
1333+ }
12961334 }.flatten
12971335 } yield awaited
12981336
1299- def performStep (stepType : String ): IO [Unit ] =
1337+ def performStep (stepType : String , addedDepth : Int ): IO [Unit ] =
13001338 for {
13011339 nextContinue <- Deferred [IO , Unit ]
13021340 nextAwaitStarted <- Deferred [IO , Unit ]
@@ -1310,23 +1348,31 @@ object Parse {
13101348 case Running => Running -> IO .unit
13111349 case Stopped (whenContinued, _) =>
13121350 Stopped (nextContinue, nextAwaitStarted) -> (
1313- whenContinued.complete(()) *> // wake up await-ers
1314- nextAwaitStarted.get // block until next await is invoked
1351+ for {
1352+ d <- currentDepth.get
1353+ _ <- stopTarget.set(Some ((d + addedDepth, stepType)))
1354+ _ <- whenContinued.complete(())
1355+ _ <- nextAwaitStarted.get
1356+ } yield ()
13151357 )
13161358 }.flatten
13171359 } yield ()
13181360
1319- def stepOver (): IO [Unit ] = performStep(" stepOver" )
1320- def stepIn (): IO [Unit ] = performStep(" stepIn" )
1321- def stepOut (): IO [Unit ] = performStep(" stepOut" )
1361+ def stepOver (): IO [Unit ] = performStep(" stepOver" , 0 )
1362+ def stepIn (): IO [Unit ] = performStep(" stepIn" , 1 )
1363+ def stepOut (): IO [Unit ] = performStep(" stepOut" , - 1 )
1364+
1365+ // Helper functions for stepping
1366+ def setCurrentDepth (depth : Int ): IO [Unit ] = currentDepth.set(depth)
1367+ def setAwaitingKind (kind : String ): IO [Unit ] = awaitingKind.set(kind)
13221368
13231369 def continue (): IO [Unit ] =
13241370 state.modify {
13251371 case s @ AwaitingFirstAwait (waiterArrived) =>
13261372 s -> waiterArrived.get *> continue()
13271373 case Running => Running -> IO .unit
13281374 case Stopped (whenContinued, _) =>
1329- Running -> whenContinued.complete(()).void // wake up await-ers
1375+ Running -> (stopTarget.set( None ) *> whenContinued.complete(() )).void // wake up await-ers
13301376 }.flatten
13311377
13321378 def pause (): IO [Unit ] =
@@ -1383,7 +1429,22 @@ object Parse {
13831429
13841430 for {
13851431 _ <- logger.debug(" pre-control await" )
1386- isStepping <- control.await() // may block until external control says to unblock, for stepping behavior
1432+ _ <- control.setCurrentDepth(
1433+ Convert
1434+ .daffodilMaybeToOption(pstate.currentNode)
1435+ .map { node =>
1436+ var depth = 0
1437+ var n = node
1438+ while (n.diParent != null ) {
1439+ depth += 1
1440+ n = n.diParent
1441+ }
1442+ depth
1443+ }
1444+ .getOrElse(0 )
1445+ )
1446+ _ <- control.setAwaitingKind(" start" )
1447+ isStepping <- control.await()
13871448 _ <- logger.debug(" post-control await" )
13881449 location = createLocation(pstate.schemaFileLocation)
13891450 shouldBreak <- breakpoints.shouldBreak(location)
@@ -1416,7 +1477,22 @@ object Parse {
14161477
14171478 override def endElement (pstate : PState , processor : Parser ): Unit =
14181479 dispatcher.unsafeRunSync {
1419- control.await() *> // ensure no events while debugger is paused
1480+ val depth = Convert
1481+ .daffodilMaybeToOption(pstate.currentNode)
1482+ .map { node =>
1483+ var d = 0
1484+ var n = node
1485+ while (n.diParent != null ) {
1486+ d += 1
1487+ n = n.diParent
1488+ }
1489+ d
1490+ }
1491+ .getOrElse(0 )
1492+
1493+ control.setCurrentDepth(depth) *>
1494+ control.setAwaitingKind(" end" ) *>
1495+ control.await() *>
14201496 events.send(Event .EndElement (pstate.copyStateForDebugger)).void
14211497 }
14221498 }
0 commit comments