Skip to content

Commit

Permalink
feat(debugger): add Shire debugger support #183
Browse files Browse the repository at this point in the history
- Introduce ShireDebugRunner for debug execution.
- Add ShireLineBreakpointType for breakpoint management.
- Implement ShireDebuggerEditorsProvider for expression evaluation.
- Add ShireStackFrame for stack frame representation.
- Introduce ShireDebugSettings for debugger configuration.
- Add ShirePositionManagerFactory for position management.
- Register
  • Loading branch information
phodal committed Jan 7, 2025
1 parent b4491a5 commit 3b1fd51
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.phodal.shirelang.debugger

import com.intellij.execution.configurations.RunProfile
import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.configurations.RunnerSettings
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.runners.GenericProgramRunner
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.xdebugger.XDebugProcess
import com.intellij.xdebugger.XDebugProcessStarter
import com.intellij.xdebugger.XDebugSession
import com.intellij.xdebugger.XDebuggerManager
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider
import com.phodal.shirelang.run.ShireConfiguration

/// refs to: https://github.com/KronicDeth/intellij-elixir/pull/643/files#diff-b1ba5c87ca6f66a455e4c1539cb2d99a62722d067a3d9e8043b290426cea5470
class ShireDebugRunner : GenericProgramRunner<RunnerSettings>() {
override fun canRun(executorId: String, profile: RunProfile): Boolean {
return profile is ShireConfiguration
}

override fun getRunnerId(): String = RUNNER_ID

override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {
val xDebuggerManager = XDebuggerManager.getInstance(environment.project)
return xDebuggerManager.startSession(environment, object : XDebugProcessStarter() {
override fun start(session: XDebugSession): XDebugProcess {
return ShireDebugProcess(session, environment)
}
}).runContentDescriptor
}
}

class ShireDebugProcess(session: XDebugSession, environment: ExecutionEnvironment) : XDebugProcess(session) {
override fun getEditorsProvider(): XDebuggerEditorsProvider {
return ShireDebuggerEditorsProvider()
}
}

val RUNNER_ID: String = "ShireProgramRunner"
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.phodal.shirelang.debugger

import com.intellij.openapi.Disposable
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ConfigurableUi
import com.intellij.openapi.options.SimpleConfigurable
import com.intellij.openapi.util.Getter
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.panel
import com.intellij.util.xmlb.XmlSerializerUtil
import com.intellij.xdebugger.settings.DebuggerSettingsCategory
import com.intellij.xdebugger.settings.XDebuggerSettings
import javax.swing.JComponent

class ShireDebugSettings : XDebuggerSettings<ShireDebugSettings>("shire"), Getter<ShireDebugSettings> {
override fun get(): ShireDebugSettings = this
override fun getState()= this
override fun loadState(state: ShireDebugSettings) {
XmlSerializerUtil.copyBean(state, this)
}

var breakOnPanic: Boolean = true
override fun createConfigurables(category: DebuggerSettingsCategory): MutableCollection<out Configurable> {
// when(category) {
// DebuggerSettingsCategory.ROOT -> TODO()
// DebuggerSettingsCategory.GENERAL -> TODO()
// DebuggerSettingsCategory.DATA_VIEWS -> TODO()
// DebuggerSettingsCategory.STEPPING -> TODO()
// DebuggerSettingsCategory.HOTSWAP -> TODO()
// }

val config = SimpleConfigurable.create(
"ShireDebugSettings",
"Shire Debugger",
ShireDebugSettingsConfigurableUi::class.java,
this
)
return mutableListOf(config)
}

companion object {
@JvmStatic
fun getInstance(): ShireDebugSettings = getInstance(ShireDebugSettings::class.java)
}
}

class ShireDebugSettingsConfigurableUi : ConfigurableUi<ShireDebugSettings>, Disposable {
private val components: List<ShireDebuggerUiComponent> = run {
val components = mutableListOf<ShireDebuggerUiComponent>()
components.add(RsBreakOnPanicConfigurableUi())
components
}

override fun isModified(settings: ShireDebugSettings): Boolean = components.any { it.isModified(settings) }

override fun reset(settings: ShireDebugSettings) {
components.forEach { it.reset(settings) }
}

override fun apply(settings: ShireDebugSettings) {
components.forEach { it.apply(settings) }
}

override fun getComponent(): JComponent {
return panel {
for (component in components) {
component.buildUi(this)
}
}
}

override fun dispose() {
components.forEach { it.dispose() }
}
}

abstract class ShireDebuggerUiComponent: ConfigurableUi<ShireDebugSettings>, Disposable {
abstract fun buildUi(panel: Panel)

override fun getComponent(): JComponent {
return panel {
buildUi(this)
}
}

override fun dispose() {}
}

class RsBreakOnPanicConfigurableUi : ShireDebuggerUiComponent() {
private val breakOnPanicCheckBox: JBCheckBox = JBCheckBox("Debug", ShireDebugSettings.getInstance().breakOnPanic)

override fun reset(settings: ShireDebugSettings) {
breakOnPanicCheckBox.isSelected = settings.breakOnPanic
}

override fun isModified(settings: ShireDebugSettings): Boolean
= settings.breakOnPanic != breakOnPanicCheckBox.isSelected

override fun apply(settings: ShireDebugSettings) {
settings.breakOnPanic = breakOnPanicCheckBox.isSelected
}

override fun buildUi(panel: Panel) {
with(panel) {
row { cell(breakOnPanicCheckBox) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.phodal.shirelang.debugger

import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProviderBase
import com.phodal.shirelang.ShireFileType
import com.phodal.shirelang.psi.ShireFile
import com.intellij.testFramework.LightVirtualFile

class ShireDebuggerEditorsProvider: XDebuggerEditorsProviderBase() {
override fun getFileType(): FileType = ShireFileType.INSTANCE

override fun createExpressionCodeFragment(project: Project, text: String, context: PsiElement?, isPhysical: Boolean): PsiFile {
val name = "fragment" + ShireFileType.INSTANCE.defaultExtension
val viewProvider = PsiManagerEx.getInstanceEx(project).fileManager.createFileViewProvider(
LightVirtualFile(name, ShireFileType.INSTANCE, text), isPhysical)
return ShireFile(viewProvider)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.phodal.shirelang.debugger

import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.xdebugger.breakpoints.XLineBreakpointTypeBase
import com.phodal.shirelang.ShireFileType

class ShireLineBreakpointType : XLineBreakpointTypeBase(ID, TITLE, ShireDebuggerEditorsProvider()) {
override fun canPutAt(file: VirtualFile, line: Int, project: Project): Boolean {
return canPutAt(project, file, line)
}

fun canPutAt(project: Project, file: VirtualFile, line: Int): Boolean {
return (FileTypeRegistry.getInstance().isFileOfType(file, ShireFileType.INSTANCE))
}


companion object {
private const val ID = "the-shire-line"
private const val TITLE = "Shire Breakpoints"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.phodal.shirelang.debugger

import com.intellij.debugger.MultiRequestPositionManager
import com.intellij.debugger.PositionManager
import com.intellij.debugger.PositionManagerFactory
import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.DebugProcess
import com.intellij.debugger.engine.PositionManagerWithMultipleStackFrames
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.requests.ClassPrepareRequestor
import com.intellij.openapi.fileTypes.FileType
import com.intellij.util.ThreeState
import com.sun.jdi.Location
import com.sun.jdi.ReferenceType
import com.sun.jdi.request.ClassPrepareRequest
import com.phodal.shirelang.ShireFileType

class ShirePositionManagerFactory : PositionManagerFactory() {
override fun createPositionManager(process: DebugProcess): PositionManager? {
return ShirePositionManager(process)
}
}

class ShirePositionManager(private val process: DebugProcess) : MultiRequestPositionManager,
PositionManagerWithMultipleStackFrames {
override fun getAcceptedFileTypes(): Set<FileType> = setOf(ShireFileType.INSTANCE)
override fun getSourcePosition(location: Location?): SourcePosition? {
return null
}

override fun getAllClasses(position: SourcePosition): MutableList<ReferenceType> {
return mutableListOf()
}

override fun locationsOfLine(type: ReferenceType, sourcePosition: SourcePosition): MutableList<Location> {
return mutableListOf()
}

override fun createPrepareRequest(
requestor: ClassPrepareRequestor,
sourcePosition: SourcePosition,
): ClassPrepareRequest? {
return null
}

override fun evaluateCondition(
context: EvaluationContext,
frame: StackFrameProxyImpl,
location: Location,
expression: String,
): ThreeState {
return ThreeState.UNSURE
}

override fun createPrepareRequests(
requestor: ClassPrepareRequestor, position: SourcePosition,
): MutableList<ClassPrepareRequest> {
// val file = position.file
return mutableListOf()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.phodal.shirelang.debugger

import com.intellij.openapi.Disposable
import com.intellij.xdebugger.frame.XStackFrame

class ShireStackFrame(
val functionName: String,
val shireStackFrame: ShireStackFrame?,
) : XStackFrame(), Disposable {
override fun dispose() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ class ShireProgramRunner : GenericProgramRunner<RunnerSettings>(), Disposable {
Disposer.register(ShirePluginDisposable.getInstance(), this)
}

override fun getRunnerId(): String = Companion.RUNNER_ID
override fun getRunnerId(): String = RUNNER_ID

override fun canRun(executorId: String, profile: RunProfile) = profile is ShireConfiguration
override fun canRun(executorId: String, profile: RunProfile): Boolean {
return profile is ShireConfiguration
}

override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {
if (environment.runProfile !is ShireConfiguration) return null
Expand Down
7 changes: 7 additions & 0 deletions shirelang/src/main/resources/com.phodal.shirelang.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@
<configurationType implementation="com.phodal.shirelang.run.ShireConfigurationType"/>
<programRunner implementation="com.phodal.shirelang.run.ShireProgramRunner"/>

<!-- Debugger -->
<programRunner implementation="com.phodal.shirelang.debugger.ShireDebugRunner"/>
<xdebugger.breakpointType implementation="com.phodal.shirelang.debugger.ShireLineBreakpointType"/>
<xdebugger.settings implementation="com.phodal.shirelang.debugger.ShireDebugSettings"/>

<debugger.positionManagerFactory implementation="com.phodal.shirelang.debugger.ShirePositionManagerFactory"/>

<runConfigurationBeforeRunProviderDelegate
implementation="com.phodal.shirelang.run.ShireBeforeRunProviderDelegate"/>
<runConfigurationProducer implementation="com.phodal.shirelang.run.ShireRunConfigurationProducer"/>
Expand Down

0 comments on commit 3b1fd51

Please sign in to comment.