Skip to content

Commit 446a246

Browse files
committed
feat(intellij): support for pause and exception break points in debugger
1 parent 420a8a7 commit 446a246

22 files changed

+319
-107
lines changed

intellij-client/build.gradle.kts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import org.jetbrains.intellij.platform.gradle.Constants.Constraints
44
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
55
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
66
import org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask
7+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
8+
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
9+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
710

811
plugins {
912
alias(libs.plugins.kotlin)
@@ -169,9 +172,17 @@ tasks {
169172
}
170173

171174

175+
tasks.withType<KotlinCompile> {
176+
compilerOptions {
177+
jvmTarget.set(JvmTarget.JVM_21)
178+
languageVersion.set(KotlinVersion.KOTLIN_2_1)
179+
apiVersion.set(KotlinVersion.KOTLIN_2_1)
180+
}
181+
}
182+
172183
// Configure UI tests plugin
173184
// Read more: https://github.com/JetBrains/intellij-ui-test-robot
174-
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
185+
@Suppress("unused") val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
175186
task {
176187
jvmArgumentProviders += CommandLineArgumentProvider {
177188
listOf(
@@ -190,13 +201,13 @@ val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
190201
}
191202
}
192203

193-
val runIdePyCharmProf by intellijPlatformTesting.runIde.registering {
204+
@Suppress("unused") val runIdePyCharmProf by intellijPlatformTesting.runIde.registering {
194205
type = IntelliJPlatformType.PyCharmProfessional
195206

196207
prepareSandboxTask(prepareSandboxConfig)
197208
}
198209

199-
val runIdeIntellijIdeaC by intellijPlatformTesting.runIde.registering {
210+
@Suppress("unused") val runIdeIntellijIdeaC by intellijPlatformTesting.runIde.registering {
200211
type = IntelliJPlatformType.IntellijIdeaCommunity
201212

202213
prepareSandboxTask(prepareSandboxConfig)

intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeBreakpointHandler.kt

Lines changed: 0 additions & 18 deletions
This file was deleted.

intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeBreakpointProperties.kt

Lines changed: 0 additions & 14 deletions
This file was deleted.

intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeDebugProcess.kt

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ package dev.robotcode.robotcode4ij.debugging
33
import com.intellij.execution.ExecutionResult
44
import com.intellij.execution.process.ProcessHandler
55
import com.intellij.execution.ui.ExecutionConsole
6-
import com.intellij.openapi.ui.MessageType
76
import com.intellij.openapi.vfs.VirtualFile
87
import com.intellij.xdebugger.XDebugProcess
98
import com.intellij.xdebugger.XDebugSession
109
import com.intellij.xdebugger.XSourcePosition
10+
import com.intellij.xdebugger.breakpoints.XBreakpoint
1111
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
1212
import com.intellij.xdebugger.breakpoints.XLineBreakpoint
1313
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider
1414
import com.intellij.xdebugger.frame.XSuspendContext
1515
import com.jetbrains.rd.util.lifetime.Lifetime
1616
import com.jetbrains.rd.util.threading.coroutines.adviseSuspend
17+
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeExceptionBreakpointHandler
18+
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeExceptionBreakpointProperties
19+
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeLineBreakpointHandler
20+
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeLineBreakpointProperties
1721
import dev.robotcode.robotcode4ij.execution.RobotCodeRunProfileState
1822
import kotlinx.coroutines.Dispatchers
1923
import kotlinx.coroutines.future.await
@@ -22,7 +26,7 @@ import kotlinx.coroutines.sync.Mutex
2226
import kotlinx.coroutines.sync.withLock
2327
import org.eclipse.lsp4j.debug.ContinueArguments
2428
import org.eclipse.lsp4j.debug.NextArguments
25-
import org.eclipse.lsp4j.debug.OutputEventArgumentsCategory
29+
import org.eclipse.lsp4j.debug.PauseArguments
2630
import org.eclipse.lsp4j.debug.SetBreakpointsArguments
2731
import org.eclipse.lsp4j.debug.Source
2832
import org.eclipse.lsp4j.debug.SourceBreakpoint
@@ -50,26 +54,11 @@ class RobotCodeDebugProcess(
5054
}
5155

5256
init {
57+
session.setPauseActionSupported(true)
5358
state.afterInitialize.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) {
5459
runBlocking { sendBreakpointRequest() }
5560
}
56-
debugClient.onStopped.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) { args ->
57-
handleOnStopped(args)
58-
}
59-
// debugClient.onOutput.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) { args ->
60-
//
61-
// session.reportMessage(
62-
// args.output, when (args.category) {
63-
// OutputEventArgumentsCategory.STDOUT, OutputEventArgumentsCategory.CONSOLE -> MessageType.INFO
64-
// OutputEventArgumentsCategory.STDERR -> MessageType.ERROR
65-
// else -> MessageType.WARNING
66-
// }
67-
// )
68-
// }
69-
70-
// debugClient.onTerminated.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) {
71-
// session.stop()
72-
// }
61+
debugClient.onStopped.adviseSuspend(Lifetime.Eternal, Dispatchers.IO, this::handleOnStopped)
7362
}
7463

7564
private suspend fun createRobotCodeSuspendContext(threadId: Int): RobotCodeSuspendContext {
@@ -89,8 +78,7 @@ class RobotCodeDebugProcess(
8978
if (bp is LineBreakpointInfo) {
9079
if (!session.breakpointReached(
9180
bp.breakpoint, null, createRobotCodeSuspendContext(
92-
args
93-
.threadId
81+
args.threadId
9482
)
9583
)
9684
) {
@@ -103,9 +91,17 @@ class RobotCodeDebugProcess(
10391
}
10492
}
10593

106-
"exception" -> {
107-
// TODO session.exceptionCaught()
108-
session.positionReached(createRobotCodeSuspendContext(args.threadId))
94+
"exception" -> { // TODO session.exceptionCaught()
95+
if (!session.breakpointReached(
96+
exceptionBreakpoints.first().breakpoint,
97+
null,
98+
createRobotCodeSuspendContext(args.threadId)
99+
)
100+
) {
101+
debugServer.continue_(ContinueArguments().apply {
102+
threadId = args.threadId
103+
}).await()
104+
}
109105
}
110106

111107
else -> {
@@ -116,18 +112,26 @@ class RobotCodeDebugProcess(
116112
}
117113

118114
private open class BreakPointInfo(val line: Int, var file: VirtualFile, var id: Int? = null)
119-
private class LineBreakpointInfo(val breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>, id: Int? = null) :
120-
BreakPointInfo(breakpoint.line, breakpoint.sourcePosition!!.file, id)
115+
private class LineBreakpointInfo(
116+
val breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>, id: Int? = null
117+
) : BreakPointInfo(breakpoint.line, breakpoint.sourcePosition!!.file, id)
118+
119+
private class ExceptionBreakpointInfo(
120+
val breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>, id: Int? = null
121+
)
121122

122123
private class OneTimeBreakpointInfo(val position: XSourcePosition, id: Int? = null) :
123124
BreakPointInfo(position.line, position.file, id)
124125

126+
private val exceptionBreakpoints = mutableListOf<ExceptionBreakpointInfo>()
127+
125128
private val breakpoints = mutableListOf<BreakPointInfo>()
126129
private val breakpointMap = mutableMapOf<VirtualFile, MutableMap<Int, BreakPointInfo>>()
127130
private val breakpointsMapMutex = Mutex()
128131

129132
private val editorsProvider = RobotCodeXDebuggerEditorsProvider()
130-
private val breakpointHandler = RobotCodeBreakpointHandler(this)
133+
private val breakpointHandler = RobotCodeLineBreakpointHandler(this)
134+
private val exceptionBreakpointHandler = RobotCodeExceptionBreakpointHandler(this)
131135

132136
override fun getEditorsProvider(): XDebuggerEditorsProvider {
133137
return editorsProvider
@@ -146,10 +150,26 @@ class RobotCodeDebugProcess(
146150
}
147151

148152
override fun getBreakpointHandlers(): Array<out XBreakpointHandler<*>?> {
149-
return arrayOf(breakpointHandler)
153+
return arrayOf(breakpointHandler, exceptionBreakpointHandler)
154+
}
155+
156+
fun registerExceptionBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
157+
runBlocking {
158+
breakpointsMapMutex.withLock {
159+
exceptionBreakpoints.add(ExceptionBreakpointInfo(breakpoint))
160+
}
161+
}
150162
}
151163

152-
fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>) {
164+
fun unregisterExceptionBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
165+
runBlocking {
166+
breakpointsMapMutex.withLock {
167+
exceptionBreakpoints.removeIf { it.breakpoint == breakpoint }
168+
}
169+
}
170+
}
171+
172+
fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
153173
runBlocking {
154174
breakpointsMapMutex.withLock {
155175
breakpoint.sourcePosition?.let {
@@ -165,7 +185,7 @@ class RobotCodeDebugProcess(
165185
}
166186
}
167187

168-
fun unregisterBreakpoint(breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>) {
188+
fun unregisterBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
169189
runBlocking {
170190
breakpointsMapMutex.withLock {
171191
breakpoint.sourcePosition?.let {
@@ -314,4 +334,10 @@ class RobotCodeDebugProcess(
314334
resume(context)
315335
}
316336
}
337+
338+
override fun startPausing() {
339+
runBlocking {
340+
debugServer.pause(PauseArguments().apply { threadId = 0 }).await()
341+
}
342+
}
317343
}

intellij-client/src/main/kotlin/dev/robotcode/robotcode4ij/debugging/RobotCodeDebugProgramRunner.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class RobotCodeDebugProgramRunner : AsyncProgramRunner<RunnerSettings>() {
4141
return RobotCodeDebugProcess(session, result, state)
4242
}
4343
})
44+
4445
return session.runContentDescriptor
4546
}
4647
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.robotcode.robotcode4ij.debugging.breakpoints
2+
3+
import com.intellij.xdebugger.breakpoints.XBreakpoint
4+
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
5+
import dev.robotcode.robotcode4ij.debugging.RobotCodeDebugProcess
6+
7+
class RobotCodeExceptionBreakpointHandler(val process: RobotCodeDebugProcess) :
8+
XBreakpointHandler<XBreakpoint<RobotCodeExceptionBreakpointProperties>>(RobotCodeExceptionBreakpointType::class.java) {
9+
override fun registerBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
10+
process.registerExceptionBreakpoint(breakpoint)
11+
}
12+
13+
override fun unregisterBreakpoint(
14+
breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>, temporary: Boolean
15+
) {
16+
process.unregisterExceptionBreakpoint(breakpoint)
17+
}
18+
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.robotcode.robotcode4ij.debugging.breakpoints
2+
3+
import com.intellij.xdebugger.breakpoints.XBreakpointProperties
4+
5+
class RobotCodeExceptionBreakpointProperties : XBreakpointProperties<RobotCodeExceptionBreakpointProperties>() {
6+
override fun getState(): RobotCodeExceptionBreakpointProperties? {
7+
return this
8+
}
9+
10+
override fun loadState(state: RobotCodeExceptionBreakpointProperties) {
11+
TODO("Not yet implemented")
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package dev.robotcode.robotcode4ij.debugging.breakpoints
2+
3+
import com.intellij.icons.AllIcons
4+
import com.intellij.openapi.project.Project
5+
import com.intellij.xdebugger.breakpoints.XBreakpoint
6+
import com.intellij.xdebugger.breakpoints.XBreakpointType
7+
import org.jetbrains.annotations.Nls
8+
import org.jetbrains.annotations.NonNls
9+
import javax.swing.Icon
10+
import javax.swing.JComponent
11+
12+
class RobotCodeExceptionBreakpointType :
13+
XBreakpointType<XBreakpoint<RobotCodeExceptionBreakpointProperties>, RobotCodeExceptionBreakpointProperties>(
14+
ID,
15+
NAME
16+
) {
17+
18+
companion object {
19+
private const val ID = "robotcode-exception"
20+
private const val NAME = "Robot Framework Exception Breakpoint"
21+
}
22+
23+
override fun getDisplayText(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>): @Nls String? {
24+
return "Any Exception"
25+
}
26+
27+
override fun getEnabledIcon(): Icon {
28+
return AllIcons.Debugger.Db_exception_breakpoint
29+
}
30+
31+
override fun getDisabledIcon(): Icon {
32+
return AllIcons.Debugger.Db_disabled_exception_breakpoint
33+
}
34+
35+
override fun createProperties(): RobotCodeExceptionBreakpointProperties? {
36+
return RobotCodeExceptionBreakpointProperties()
37+
}
38+
39+
override fun addBreakpoint(
40+
project: Project?,
41+
parentComponent: JComponent?
42+
): XBreakpoint<RobotCodeExceptionBreakpointProperties>? {
43+
return super.addBreakpoint(project, parentComponent)
44+
}
45+
46+
override fun getBreakpointsDialogHelpTopic(): @NonNls String? {
47+
return "reference.dialogs.breakpoints"
48+
}
49+
50+
override fun createDefaultBreakpoint(creator: XBreakpointCreator<RobotCodeExceptionBreakpointProperties?>): XBreakpoint<RobotCodeExceptionBreakpointProperties?>? {
51+
var breakpoint = creator.createBreakpoint(RobotCodeExceptionBreakpointProperties())
52+
breakpoint.isEnabled = true
53+
return breakpoint
54+
}
55+
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.robotcode.robotcode4ij.debugging.breakpoints
2+
3+
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
4+
import com.intellij.xdebugger.breakpoints.XLineBreakpoint
5+
import dev.robotcode.robotcode4ij.debugging.RobotCodeDebugProcess
6+
7+
class RobotCodeLineBreakpointHandler(val process: RobotCodeDebugProcess) :
8+
XBreakpointHandler<XLineBreakpoint<RobotCodeLineBreakpointProperties>>(RobotCodeLineBreakpointType::class.java) {
9+
override fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
10+
process.registerBreakpoint(breakpoint)
11+
}
12+
13+
override fun unregisterBreakpoint(
14+
breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>,
15+
temporary: Boolean
16+
) {
17+
process.unregisterBreakpoint(breakpoint)
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dev.robotcode.robotcode4ij.debugging.breakpoints
2+
3+
import com.intellij.xdebugger.breakpoints.XBreakpointProperties
4+
5+
class RobotCodeLineBreakpointProperties : XBreakpointProperties<RobotCodeLineBreakpointProperties>() {
6+
7+
override fun getState(): RobotCodeLineBreakpointProperties {
8+
return this
9+
}
10+
11+
override fun loadState(state: RobotCodeLineBreakpointProperties) {
12+
TODO("Not yet implemented")
13+
}
14+
}

0 commit comments

Comments
 (0)