Skip to content

Commit 8d10f5c

Browse files
committed
Print next iota on debugger step
1 parent 32d1bb0 commit 8d10f5c

File tree

7 files changed

+126
-12
lines changed

7 files changed

+126
-12
lines changed

Common/src/main/kotlin/gay/object/hexdebug/adapter/DebugAdapter.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import gay.`object`.hexdebug.items.ItemEvaluator
1919
import gay.`object`.hexdebug.networking.HexDebugNetworking
2020
import gay.`object`.hexdebug.networking.MsgDebuggerStateS2C
2121
import gay.`object`.hexdebug.networking.MsgEvaluatorStateS2C
22+
import gay.`object`.hexdebug.networking.MsgPrintDebuggerStatusS2C
2223
import gay.`object`.hexdebug.utils.futureOf
2324
import gay.`object`.hexdebug.utils.paginate
2425
import gay.`object`.hexdebug.utils.toFuture
@@ -74,11 +75,21 @@ open class DebugAdapter(val player: ServerPlayer) : IDebugProtocolServer {
7475
HexDebugNetworking.sendToPlayer(player, MsgEvaluatorStateS2C(evalState))
7576
}
7677

78+
protected open fun printDebuggerStatus(iota: String, index: Int) {
79+
HexDebugNetworking.sendToPlayer(
80+
player, MsgPrintDebuggerStatusS2C(
81+
iota = iota,
82+
index = index,
83+
line = state.initArgs.indexToLine(index),
84+
isConnected = state.isConnected,
85+
)
86+
)
87+
}
88+
7789
fun startDebugging(args: CastArgs): Boolean {
7890
if (state is Debugging) return false
79-
state = Debugging(state, args).also {
80-
handleDebuggerStep(it.debugger.start())
81-
}
91+
val state = Debugging(state, args).also { state = it }
92+
handleDebuggerStep(state.debugger.start())
8293
return true
8394
}
8495

@@ -155,6 +166,11 @@ open class DebugAdapter(val player: ServerPlayer) : IDebugProtocolServer {
155166
}
156167

157168
sendStoppedEvent(result.reason)
169+
170+
debugger?.getNextIotaToEvaluate()?.also { (iota, index) ->
171+
printDebuggerStatus(iota, index)
172+
}
173+
158174
return view
159175
}
160176

@@ -201,7 +217,10 @@ open class DebugAdapter(val player: ServerPlayer) : IDebugProtocolServer {
201217
}
202218

203219
override fun attach(args: MutableMap<String, Any>): CompletableFuture<Void> {
204-
state.launchArgs = LaunchArgs(args)
220+
state.apply {
221+
isConnected = true
222+
launchArgs = LaunchArgs(args)
223+
}
205224
remoteProxy.initialized()
206225
player.displayClientMessage(Component.translatable("text.hexdebug.connected"), true)
207226
return futureOf()

Common/src/main/kotlin/gay/object/hexdebug/adapter/DebugAdapterState.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ sealed interface DebugAdapterState {
1212

1313
data class NotDebugging(
1414
override var isConnected: Boolean = false,
15-
override var initArgs: InitializeRequestArguments = InitializeRequestArguments(),
15+
override var initArgs: InitializeRequestArguments = defaultInitArgs(),
1616
override var launchArgs: LaunchArgs = LaunchArgs(),
1717
override val restartArgs: CastArgs? = null,
1818
) : DebugAdapterState {
@@ -34,3 +34,8 @@ sealed interface DebugAdapterState {
3434
override var launchArgs by debugger::launchArgs
3535
}
3636
}
37+
38+
fun defaultInitArgs() = InitializeRequestArguments().apply {
39+
linesStartAt1 = true
40+
columnsStartAt1 = true
41+
}

Common/src/main/kotlin/gay/object/hexdebug/config/HexDebugConfig.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,17 @@ object HexDebugConfig {
4646

4747
@Tooltip
4848
val smartDebuggerSneakScroll: Boolean = true
49+
50+
@Tooltip
51+
val debuggerDisplayMode: DebuggerDisplayMode = DebuggerDisplayMode.ENABLED
52+
53+
@Tooltip
54+
val showDebugClientLineNumber: Boolean = false
4955
}
5056
}
57+
58+
enum class DebuggerDisplayMode {
59+
DISABLED,
60+
NOT_CONNECTED,
61+
ENABLED,
62+
}

Common/src/main/kotlin/gay/object/hexdebug/debugger/HexDebugger.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ class HexDebugger(
5656
private val sourceAllocator = SourceAllocator(iotas.hashCode())
5757

5858
private val iotaMetadata = IdentityHashMap<Iota, IotaMetadata>()
59-
private val frameInvocationMetadata = IdentityHashMap<SpellContinuation, () -> IotaMetadata?>()
59+
// FIXME: this is really terrible and gross and i don't like it
60+
private val frameInvocationMetadata = IdentityHashMap<SpellContinuation, () -> Pair<Iota, IotaMetadata?>?>()
6061
private val virtualFrames = IdentityHashMap<SpellContinuation, MutableList<StackFrame>>()
6162

6263
private val breakpoints = mutableMapOf<Int, MutableMap<Int, SourceBreakpointMode>>() // source id -> line number
@@ -93,7 +94,7 @@ class HexDebugger(
9394
}
9495
pushFrame(FrameBreakpoint(stopBefore = true)).also { newCont ->
9596
frameInvocationMetadata[newCont] = {
96-
lastIota?.let { iotaMetadata[it] }?.copy(columnIndex = columnIndex)
97+
lastIota?.let { it to iotaMetadata[it]?.copy(columnIndex = columnIndex) }
9798
}
9899
}
99100
} else this
@@ -124,15 +125,15 @@ class HexDebugger(
124125
else -> null
125126
}
126127

127-
private fun getFirstIotaMetadata(continuation: NotDone): IotaMetadata? =
128+
private fun getFirstIotaMetadata(continuation: NotDone): Pair<Iota, IotaMetadata?>? =
128129
// for FrameEvaluate, show the next iota to be evaluated (if any)
129130
(continuation.frame as? FrameEvaluate)?.let(::getFirstIotaMetadata)
130131
// for everything else, show the caller if we have it
131132
?: frameInvocationMetadata[continuation]?.invoke()
132133
// otherwise show the first contained iota
133134
?: getFirstIotaMetadata(continuation.frame)
134135

135-
private fun getFirstIotaMetadata(frame: ContinuationFrame) = getIotas(frame)?.let { iotaMetadata[it.car] }
136+
private fun getFirstIotaMetadata(frame: ContinuationFrame) = getIotas(frame)?.let { it.car to iotaMetadata[it.car] }
136137

137138
// current continuation is last
138139
private fun getCallStack(current: SpellContinuation) = generateSequence(current as? NotDone) {
@@ -142,6 +143,13 @@ class HexDebugger(
142143
}
143144
}.toList().asReversed()
144145

146+
/** (iota, index) */
147+
fun getNextIotaToEvaluate(): Pair<String, Int>? {
148+
val continuation = nextContinuation as? NotDone ?: return null
149+
val (iota, meta) = getFirstIotaMetadata(continuation) ?: return null
150+
return iotaToString(iota, isSource = false) to (meta?.lineIndex ?: -1)
151+
}
152+
145153
fun getStackFrames(): Sequence<StackFrame> {
146154
var frameId = 1
147155
var virtualFrameId = (callStack.size + 1).ceilToPow(10)
@@ -150,7 +158,7 @@ class HexDebugger(
150158
StackFrame().apply {
151159
id = frameId++
152160
name = "[$id] ${continuation.frame.name}"
153-
setSourceAndPosition(initArgs, getFirstIotaMetadata(continuation))
161+
setSourceAndPosition(initArgs, getFirstIotaMetadata(continuation)?.second)
154162
}
155163
) + virtualFrames[continuation]?.map {
156164
it.apply {
@@ -549,7 +557,7 @@ class HexDebugger(
549557
// insert a virtual FrameFinishEval if OpEval didn't (ie. if we did a TCO)
550558
if (launchArgs.showTailCallFrames && vm.debugCastEnv.lastEvaluatedAction is OpEval) {
551559
val invokeMeta = iotaMetadata[castResult.cast]
552-
val nextInvokeMeta = frameInvocationMetadata[newContinuation.next]?.invoke()
560+
val nextInvokeMeta = frameInvocationMetadata[newContinuation.next]?.invoke()?.second
553561
if (invokeMeta != null && invokeMeta != nextInvokeMeta) {
554562
virtualFrames.getOrPut(continuation.next) { mutableListOf() }.add(
555563
StackFrame().apply {
@@ -712,7 +720,7 @@ class HexDebugger(
712720

713721
private fun trySetIotaOverride(continuation: SpellContinuation, castResult: CastResult): Boolean {
714722
return if (continuation !in frameInvocationMetadata && continuation is NotDone) {
715-
frameInvocationMetadata[continuation] = { iotaMetadata[castResult.cast] }
723+
frameInvocationMetadata[continuation] = { castResult.cast to iotaMetadata[castResult.cast] }
716724
true
717725
} else false
718726
}

Common/src/main/kotlin/gay/object/hexdebug/networking/HexDebugNetworking.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ object HexDebugNetworking {
1313
CHANNEL.register(MsgDebugAdapterProxyS2C::class.java, MsgDebugAdapterProxyS2C::encode, ::MsgDebugAdapterProxyS2C, MsgDebugAdapterProxyS2C::apply)
1414
CHANNEL.register(MsgDebuggerStateS2C::class.java, MsgDebuggerStateS2C::encode, ::MsgDebuggerStateS2C, MsgDebuggerStateS2C::apply)
1515
CHANNEL.register(MsgEvaluatorStateS2C::class.java, MsgEvaluatorStateS2C::encode, ::MsgEvaluatorStateS2C, MsgEvaluatorStateS2C::apply)
16+
CHANNEL.register(MsgPrintDebuggerStatusS2C::class.java, MsgPrintDebuggerStatusS2C::encode, ::MsgPrintDebuggerStatusS2C, MsgPrintDebuggerStatusS2C::apply)
1617
}
1718

1819
fun <T> sendToServer(message: T) = CHANNEL.sendToServer(message)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package gay.`object`.hexdebug.networking
2+
3+
import dev.architectury.networking.NetworkManager.PacketContext
4+
import gay.`object`.hexdebug.HexDebug
5+
import gay.`object`.hexdebug.config.DebuggerDisplayMode
6+
import gay.`object`.hexdebug.config.HexDebugConfig
7+
import net.minecraft.network.FriendlyByteBuf
8+
import net.minecraft.network.chat.Component
9+
import java.util.function.Supplier
10+
11+
// we need a message for this because the client config isn't available on the server
12+
data class MsgPrintDebuggerStatusS2C(
13+
val iota: String,
14+
val index: Int,
15+
val line: Int,
16+
val isConnected: Boolean,
17+
) {
18+
constructor(buf: FriendlyByteBuf) : this(
19+
buf.readUtf(),
20+
buf.readInt(),
21+
buf.readInt(),
22+
buf.readBoolean(),
23+
)
24+
25+
fun encode(buf: FriendlyByteBuf) {
26+
buf.writeUtf(iota)
27+
buf.writeInt(index)
28+
buf.writeInt(line)
29+
buf.writeBoolean(isConnected)
30+
}
31+
32+
fun apply(supplier: Supplier<PacketContext>) = supplier.get().also { ctx ->
33+
ctx.queue {
34+
HexDebug.LOGGER.debug("Client received packet: {}", this)
35+
36+
val config = HexDebugConfig.get().client
37+
val shouldPrint = when (config.debuggerDisplayMode) {
38+
DebuggerDisplayMode.DISABLED -> false
39+
DebuggerDisplayMode.NOT_CONNECTED -> !isConnected
40+
DebuggerDisplayMode.ENABLED -> true
41+
}
42+
43+
if (shouldPrint) {
44+
ctx.player.displayClientMessage(
45+
Component.translatable(
46+
"text.hexdebug.debugger_stopped",
47+
if (config.showDebugClientLineNumber) line else index,
48+
iota,
49+
),
50+
true,
51+
)
52+
}
53+
}
54+
}
55+
}

Common/src/main/resources/assets/hexdebug/lang/en_us.flatten.json5

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
connected: "Debug client connected!",
1212
no_session: "Debug session is not running.",
1313
thwack: "Thwack!",
14+
// Next Iota: [{index}] {iota}
15+
debugger_stopped: "Next Iota: [%d] %s",
1416
},
1517

1618
"autoconfig.hexdebug": {
@@ -32,6 +34,18 @@
3234
"": "Smart Debugger Sneak-Scroll",
3335
"@Tooltip": "If a hex is not currently being debugged and a Debugger is in your main hand, prefer shift-scrolling whatever item is in your offhand (eg. a spellbook).",
3436
},
37+
debuggerDisplayMode: {
38+
"": "Debugger Display Mode",
39+
"@Tooltip": "\
40+
Changes when the Debugger should print the next iota to be evaluated.\n\
41+
DISABLED: Never show Debugger status messages.\n\
42+
NOT_CONNECTED: Only show Debugger status messages if a debug client is not connected.\n\
43+
ENABLED: Always show Debugger status messages. (default)",
44+
},
45+
showDebugClientLineNumber: {
46+
"": "Show Debug Client Line Number",
47+
"@Tooltip": "If true, show the line number (usually 1-indexed) from the debug client (eg. VSCode) in Debugger status messages; otherwise, show the list index (0-indexed)."
48+
}
3549
},
3650
},
3751
},

0 commit comments

Comments
 (0)