Skip to content

Commit

Permalink
feat(intellij): support for pause and exception break points in debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
d-biehl committed Jan 15, 2025
1 parent 420a8a7 commit 446a246
Show file tree
Hide file tree
Showing 22 changed files with 319 additions and 107 deletions.
17 changes: 14 additions & 3 deletions intellij-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import org.jetbrains.intellij.platform.gradle.Constants.Constraints
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.kotlin)
Expand Down Expand Up @@ -169,9 +172,17 @@ tasks {
}


tasks.withType<KotlinCompile> {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
languageVersion.set(KotlinVersion.KOTLIN_2_1)
apiVersion.set(KotlinVersion.KOTLIN_2_1)
}
}

// Configure UI tests plugin
// Read more: https://github.com/JetBrains/intellij-ui-test-robot
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
@Suppress("unused") val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
task {
jvmArgumentProviders += CommandLineArgumentProvider {
listOf(
Expand All @@ -190,13 +201,13 @@ val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
}
}

val runIdePyCharmProf by intellijPlatformTesting.runIde.registering {
@Suppress("unused") val runIdePyCharmProf by intellijPlatformTesting.runIde.registering {
type = IntelliJPlatformType.PyCharmProfessional

prepareSandboxTask(prepareSandboxConfig)
}

val runIdeIntellijIdeaC by intellijPlatformTesting.runIde.registering {
@Suppress("unused") val runIdeIntellijIdeaC by intellijPlatformTesting.runIde.registering {
type = IntelliJPlatformType.IntellijIdeaCommunity

prepareSandboxTask(prepareSandboxConfig)
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ package dev.robotcode.robotcode4ij.debugging
import com.intellij.execution.ExecutionResult
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.ui.ExecutionConsole
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.xdebugger.XDebugProcess
import com.intellij.xdebugger.XDebugSession
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.breakpoints.XBreakpoint
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
import com.intellij.xdebugger.breakpoints.XLineBreakpoint
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider
import com.intellij.xdebugger.frame.XSuspendContext
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.threading.coroutines.adviseSuspend
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeExceptionBreakpointHandler
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeExceptionBreakpointProperties
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeLineBreakpointHandler
import dev.robotcode.robotcode4ij.debugging.breakpoints.RobotCodeLineBreakpointProperties
import dev.robotcode.robotcode4ij.execution.RobotCodeRunProfileState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.await
Expand All @@ -22,7 +26,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.eclipse.lsp4j.debug.ContinueArguments
import org.eclipse.lsp4j.debug.NextArguments
import org.eclipse.lsp4j.debug.OutputEventArgumentsCategory
import org.eclipse.lsp4j.debug.PauseArguments
import org.eclipse.lsp4j.debug.SetBreakpointsArguments
import org.eclipse.lsp4j.debug.Source
import org.eclipse.lsp4j.debug.SourceBreakpoint
Expand Down Expand Up @@ -50,26 +54,11 @@ class RobotCodeDebugProcess(
}

init {
session.setPauseActionSupported(true)
state.afterInitialize.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) {
runBlocking { sendBreakpointRequest() }
}
debugClient.onStopped.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) { args ->
handleOnStopped(args)
}
// debugClient.onOutput.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) { args ->
//
// session.reportMessage(
// args.output, when (args.category) {
// OutputEventArgumentsCategory.STDOUT, OutputEventArgumentsCategory.CONSOLE -> MessageType.INFO
// OutputEventArgumentsCategory.STDERR -> MessageType.ERROR
// else -> MessageType.WARNING
// }
// )
// }

// debugClient.onTerminated.adviseSuspend(Lifetime.Eternal, Dispatchers.IO) {
// session.stop()
// }
debugClient.onStopped.adviseSuspend(Lifetime.Eternal, Dispatchers.IO, this::handleOnStopped)
}

private suspend fun createRobotCodeSuspendContext(threadId: Int): RobotCodeSuspendContext {
Expand All @@ -89,8 +78,7 @@ class RobotCodeDebugProcess(
if (bp is LineBreakpointInfo) {
if (!session.breakpointReached(
bp.breakpoint, null, createRobotCodeSuspendContext(
args
.threadId
args.threadId
)
)
) {
Expand All @@ -103,9 +91,17 @@ class RobotCodeDebugProcess(
}
}

"exception" -> {
// TODO session.exceptionCaught()
session.positionReached(createRobotCodeSuspendContext(args.threadId))
"exception" -> { // TODO session.exceptionCaught()
if (!session.breakpointReached(
exceptionBreakpoints.first().breakpoint,
null,
createRobotCodeSuspendContext(args.threadId)
)
) {
debugServer.continue_(ContinueArguments().apply {
threadId = args.threadId
}).await()
}
}

else -> {
Expand All @@ -116,18 +112,26 @@ class RobotCodeDebugProcess(
}

private open class BreakPointInfo(val line: Int, var file: VirtualFile, var id: Int? = null)
private class LineBreakpointInfo(val breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>, id: Int? = null) :
BreakPointInfo(breakpoint.line, breakpoint.sourcePosition!!.file, id)
private class LineBreakpointInfo(
val breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>, id: Int? = null
) : BreakPointInfo(breakpoint.line, breakpoint.sourcePosition!!.file, id)

private class ExceptionBreakpointInfo(
val breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>, id: Int? = null
)

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

private val exceptionBreakpoints = mutableListOf<ExceptionBreakpointInfo>()

private val breakpoints = mutableListOf<BreakPointInfo>()
private val breakpointMap = mutableMapOf<VirtualFile, MutableMap<Int, BreakPointInfo>>()
private val breakpointsMapMutex = Mutex()

private val editorsProvider = RobotCodeXDebuggerEditorsProvider()
private val breakpointHandler = RobotCodeBreakpointHandler(this)
private val breakpointHandler = RobotCodeLineBreakpointHandler(this)
private val exceptionBreakpointHandler = RobotCodeExceptionBreakpointHandler(this)

override fun getEditorsProvider(): XDebuggerEditorsProvider {
return editorsProvider
Expand All @@ -146,10 +150,26 @@ class RobotCodeDebugProcess(
}

override fun getBreakpointHandlers(): Array<out XBreakpointHandler<*>?> {
return arrayOf(breakpointHandler)
return arrayOf(breakpointHandler, exceptionBreakpointHandler)
}

fun registerExceptionBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
runBlocking {
breakpointsMapMutex.withLock {
exceptionBreakpoints.add(ExceptionBreakpointInfo(breakpoint))
}
}
}

fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>) {
fun unregisterExceptionBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
runBlocking {
breakpointsMapMutex.withLock {
exceptionBreakpoints.removeIf { it.breakpoint == breakpoint }
}
}
}

fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
runBlocking {
breakpointsMapMutex.withLock {
breakpoint.sourcePosition?.let {
Expand All @@ -165,7 +185,7 @@ class RobotCodeDebugProcess(
}
}

fun unregisterBreakpoint(breakpoint: XLineBreakpoint<RobotCodeBreakpointProperties>) {
fun unregisterBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
runBlocking {
breakpointsMapMutex.withLock {
breakpoint.sourcePosition?.let {
Expand Down Expand Up @@ -314,4 +334,10 @@ class RobotCodeDebugProcess(
resume(context)
}
}

override fun startPausing() {
runBlocking {
debugServer.pause(PauseArguments().apply { threadId = 0 }).await()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class RobotCodeDebugProgramRunner : AsyncProgramRunner<RunnerSettings>() {
return RobotCodeDebugProcess(session, result, state)
}
})

return session.runContentDescriptor
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.robotcode.robotcode4ij.debugging.breakpoints

import com.intellij.xdebugger.breakpoints.XBreakpoint
import com.intellij.xdebugger.breakpoints.XBreakpointHandler
import dev.robotcode.robotcode4ij.debugging.RobotCodeDebugProcess

class RobotCodeExceptionBreakpointHandler(val process: RobotCodeDebugProcess) :
XBreakpointHandler<XBreakpoint<RobotCodeExceptionBreakpointProperties>>(RobotCodeExceptionBreakpointType::class.java) {
override fun registerBreakpoint(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>) {
process.registerExceptionBreakpoint(breakpoint)
}

override fun unregisterBreakpoint(
breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>, temporary: Boolean
) {
process.unregisterExceptionBreakpoint(breakpoint)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.robotcode.robotcode4ij.debugging.breakpoints

import com.intellij.xdebugger.breakpoints.XBreakpointProperties

class RobotCodeExceptionBreakpointProperties : XBreakpointProperties<RobotCodeExceptionBreakpointProperties>() {
override fun getState(): RobotCodeExceptionBreakpointProperties? {
return this
}

override fun loadState(state: RobotCodeExceptionBreakpointProperties) {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package dev.robotcode.robotcode4ij.debugging.breakpoints

import com.intellij.icons.AllIcons
import com.intellij.openapi.project.Project
import com.intellij.xdebugger.breakpoints.XBreakpoint
import com.intellij.xdebugger.breakpoints.XBreakpointType
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import javax.swing.Icon
import javax.swing.JComponent

class RobotCodeExceptionBreakpointType :
XBreakpointType<XBreakpoint<RobotCodeExceptionBreakpointProperties>, RobotCodeExceptionBreakpointProperties>(
ID,
NAME
) {

companion object {
private const val ID = "robotcode-exception"
private const val NAME = "Robot Framework Exception Breakpoint"
}

override fun getDisplayText(breakpoint: XBreakpoint<RobotCodeExceptionBreakpointProperties>): @Nls String? {
return "Any Exception"
}

override fun getEnabledIcon(): Icon {
return AllIcons.Debugger.Db_exception_breakpoint
}

override fun getDisabledIcon(): Icon {
return AllIcons.Debugger.Db_disabled_exception_breakpoint
}

override fun createProperties(): RobotCodeExceptionBreakpointProperties? {
return RobotCodeExceptionBreakpointProperties()
}

override fun addBreakpoint(
project: Project?,
parentComponent: JComponent?
): XBreakpoint<RobotCodeExceptionBreakpointProperties>? {
return super.addBreakpoint(project, parentComponent)
}

override fun getBreakpointsDialogHelpTopic(): @NonNls String? {
return "reference.dialogs.breakpoints"
}

override fun createDefaultBreakpoint(creator: XBreakpointCreator<RobotCodeExceptionBreakpointProperties?>): XBreakpoint<RobotCodeExceptionBreakpointProperties?>? {
var breakpoint = creator.createBreakpoint(RobotCodeExceptionBreakpointProperties())
breakpoint.isEnabled = true
return breakpoint
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.robotcode.robotcode4ij.debugging.breakpoints

import com.intellij.xdebugger.breakpoints.XBreakpointHandler
import com.intellij.xdebugger.breakpoints.XLineBreakpoint
import dev.robotcode.robotcode4ij.debugging.RobotCodeDebugProcess

class RobotCodeLineBreakpointHandler(val process: RobotCodeDebugProcess) :
XBreakpointHandler<XLineBreakpoint<RobotCodeLineBreakpointProperties>>(RobotCodeLineBreakpointType::class.java) {
override fun registerBreakpoint(breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>) {
process.registerBreakpoint(breakpoint)
}

override fun unregisterBreakpoint(
breakpoint: XLineBreakpoint<RobotCodeLineBreakpointProperties>,
temporary: Boolean
) {
process.unregisterBreakpoint(breakpoint)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.robotcode.robotcode4ij.debugging.breakpoints

import com.intellij.xdebugger.breakpoints.XBreakpointProperties

class RobotCodeLineBreakpointProperties : XBreakpointProperties<RobotCodeLineBreakpointProperties>() {

override fun getState(): RobotCodeLineBreakpointProperties {
return this
}

override fun loadState(state: RobotCodeLineBreakpointProperties) {
TODO("Not yet implemented")
}
}
Loading

0 comments on commit 446a246

Please sign in to comment.