coroutine extention refactored to minimize brunch-related logic
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.coroutines
|
||||||
|
|
||||||
|
import com.intellij.debugger.DebuggerInvocationUtil
|
||||||
|
import com.intellij.debugger.DebuggerManagerEx
|
||||||
|
import com.intellij.debugger.actions.ThreadDumpAction
|
||||||
|
import com.intellij.debugger.impl.DebuggerSession
|
||||||
|
import com.intellij.execution.configurations.JavaParameters
|
||||||
|
import com.intellij.execution.configurations.RunConfigurationBase
|
||||||
|
import com.intellij.execution.ui.RunnerLayoutUi
|
||||||
|
import com.intellij.execution.ui.layout.PlaceInGrid
|
||||||
|
import com.intellij.execution.ui.layout.impl.RunnerContentUi
|
||||||
|
import com.intellij.execution.ui.layout.impl.RunnerLayoutUiImpl
|
||||||
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.util.registry.Registry
|
||||||
|
import com.intellij.ui.content.ContentManagerAdapter
|
||||||
|
import com.intellij.ui.content.ContentManagerEvent
|
||||||
|
import com.intellij.util.messages.MessageBusConnection
|
||||||
|
import com.intellij.xdebugger.XDebugProcess
|
||||||
|
import com.intellij.xdebugger.XDebuggerManager
|
||||||
|
import com.intellij.xdebugger.XDebuggerManagerListener
|
||||||
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
|
|
||||||
|
class CoroutineXDebuggerManagerListener(val project: Project) : XDebuggerManagerListener {
|
||||||
|
lateinit var connection: MessageBusConnection
|
||||||
|
|
||||||
|
override fun processStarted(debugProcess: XDebugProcess) {
|
||||||
|
DebuggerInvocationUtil.swingInvokeLater(project) {
|
||||||
|
val session = DebuggerManagerEx.getInstanceEx(project).context.debuggerSession
|
||||||
|
val ui = session?.xDebugSession?.ui
|
||||||
|
if (ui != null)
|
||||||
|
registerCoroutinesPanel(ui, session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processStopped(debugProcess: XDebugProcess) {
|
||||||
|
connection.disconnect()
|
||||||
|
project.listenerCreated = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds panel to XDebugSessionTab
|
||||||
|
*/
|
||||||
|
private fun registerCoroutinesPanel(ui: RunnerLayoutUi, session: DebuggerSession) {
|
||||||
|
val panel = CoroutinesPanel(session.project, session.contextManager)
|
||||||
|
val content = ui.createContent(
|
||||||
|
"CoroutinesContent", panel, "Coroutines", // TODO(design)
|
||||||
|
AllIcons.Debugger.ThreadGroup, null
|
||||||
|
)
|
||||||
|
content.isCloseable = false
|
||||||
|
ui.addContent(content, 0, PlaceInGrid.left, true)
|
||||||
|
ui.addListener(object : ContentManagerAdapter() {
|
||||||
|
override fun selectionChanged(event: ContentManagerEvent) {
|
||||||
|
if (event.content === content) {
|
||||||
|
if (content.isSelected) {
|
||||||
|
panel.setUpdateEnabled(true)
|
||||||
|
if (panel.isRefreshNeeded) {
|
||||||
|
panel.rebuildIfVisible(DebuggerSession.Event.CONTEXT)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panel.setUpdateEnabled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, content)
|
||||||
|
// add coroutine dump button: due to api problem left toolbar is copied, modified and reset to tab
|
||||||
|
val runnerContent = (ui.options as RunnerLayoutUiImpl).getData(RunnerContentUi.KEY.name) as RunnerContentUi
|
||||||
|
val modifiedActions = runnerContent.getActions(true)
|
||||||
|
val pos = modifiedActions.indexOfLast { it is ThreadDumpAction }
|
||||||
|
modifiedActions.add(pos + 1, ActionManager.getInstance().getAction("Kotlin.XDebugger.CoroutinesDump"))
|
||||||
|
ui.options.setLeftToolbar(DefaultActionGroup(modifiedActions), ActionPlaces.DEBUGGER_TOOLBAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attach() {
|
||||||
|
connection = project.messageBus.connect()
|
||||||
|
connection.subscribe(XDebuggerManager.TOPIC, this)
|
||||||
|
project.listenerCreated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var Project.listenerCreated: Boolean? by UserDataProperty(Key.create("COROUTINES_DEBUG_TAB_CREATE_LISTENER"))
|
||||||
|
|
||||||
|
internal fun isCoroutineDebuggerEnabled() = Registry.`is`("kotlin.debugger.coroutines")
|
||||||
|
|
||||||
|
internal fun <T : RunConfigurationBase<*>?> initializeCoroutineAgent(
|
||||||
|
params: JavaParameters,
|
||||||
|
it: String?,
|
||||||
|
configuration: T
|
||||||
|
): Boolean {
|
||||||
|
params.vmParametersList?.add("-javaagent:$it")
|
||||||
|
params.vmParametersList?.add("-ea")
|
||||||
|
val project = (configuration as RunConfigurationBase<*>).project
|
||||||
|
// add listener to put coroutines tab into debugger tab
|
||||||
|
if (project.listenerCreated != true) { // prevent multiple listeners creation
|
||||||
|
CoroutineXDebuggerManagerListener(project).attach()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+10
-99
@@ -4,124 +4,35 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines
|
package org.jetbrains.kotlin.idea.debugger.coroutines
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerInvocationUtil
|
|
||||||
import com.intellij.debugger.DebuggerManagerEx
|
|
||||||
import com.intellij.debugger.actions.ThreadDumpAction
|
|
||||||
import com.intellij.debugger.impl.DebuggerSession
|
|
||||||
import com.intellij.execution.RunConfigurationExtension
|
import com.intellij.execution.RunConfigurationExtension
|
||||||
import com.intellij.execution.configurations.DebuggingRunnerData
|
import com.intellij.execution.configurations.DebuggingRunnerData
|
||||||
import com.intellij.execution.configurations.JavaParameters
|
import com.intellij.execution.configurations.JavaParameters
|
||||||
import com.intellij.execution.configurations.RunConfigurationBase
|
import com.intellij.execution.configurations.RunConfigurationBase
|
||||||
import com.intellij.execution.configurations.RunnerSettings
|
import com.intellij.execution.configurations.RunnerSettings
|
||||||
import com.intellij.execution.ui.RunnerLayoutUi
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.execution.ui.layout.PlaceInGrid
|
|
||||||
import com.intellij.execution.ui.layout.impl.RunnerContentUi
|
|
||||||
import com.intellij.execution.ui.layout.impl.RunnerLayoutUiImpl
|
|
||||||
import com.intellij.icons.AllIcons
|
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
|
||||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
|
||||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.util.registry.Registry
|
|
||||||
import com.intellij.ui.content.ContentManagerAdapter
|
|
||||||
import com.intellij.ui.content.ContentManagerEvent
|
|
||||||
import com.intellij.util.messages.MessageBusConnection
|
|
||||||
import com.intellij.xdebugger.XDebugProcess
|
|
||||||
import com.intellij.xdebugger.XDebuggerManager
|
|
||||||
import com.intellij.xdebugger.XDebuggerManagerListener
|
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs coroutines debug agent and coroutines tab if `kotlinx.coroutines.debug` dependency is found
|
* Installs coroutines debug agent and coroutines tab if `kotlinx.coroutines.debug` dependency is found
|
||||||
*/
|
*/
|
||||||
@Suppress("IncompatibleAPI")
|
@Suppress("IncompatibleAPI")
|
||||||
class CoroutinesDebugConfigurationExtension : RunConfigurationExtension() {
|
class CoroutinesDebugConfigurationExtension : RunConfigurationExtension() {
|
||||||
private var Project.listenerCreated: Boolean? by UserDataProperty(Key.create("COROUTINES_DEBUG_TAB_CREATE_LISTENER"))
|
private val log = Logger.getInstance(this::class.java)
|
||||||
|
|
||||||
override fun isApplicableFor(configuration: RunConfigurationBase<*>): Boolean {
|
override fun isApplicableFor(configuration: RunConfigurationBase<*>) = isCoroutineDebuggerEnabled()
|
||||||
return Registry.`is`("kotlin.debugger.coroutines")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
|
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
|
||||||
configuration: T,
|
configuration: T,
|
||||||
params: JavaParameters?,
|
params: JavaParameters?,
|
||||||
runnerSettings: RunnerSettings?
|
runnerSettings: RunnerSettings?
|
||||||
) {
|
) {
|
||||||
if (!Registry.`is`("kotlin.debugger.coroutines")) return
|
if (!isCoroutineDebuggerEnabled()) return
|
||||||
if (runnerSettings is DebuggingRunnerData && params != null
|
if (runnerSettings is DebuggingRunnerData) {
|
||||||
&& params.classPath != null
|
try {
|
||||||
&& params.classPath.pathList.isNotEmpty()
|
val kotlinxCoroutinesClassPathLib = params?.classPath?.pathList?.first { it.contains("kotlinx-coroutines-debug") }
|
||||||
) {
|
initializeCoroutineAgent(params!!, kotlinxCoroutinesClassPathLib, configuration)
|
||||||
params.classPath.pathList.forEach {
|
} catch (e: NoSuchElementException) {
|
||||||
if (!it.contains("kotlinx-coroutines-debug")) return@forEach
|
log.warn("'kotlinx-coroutines-debug' not found in classpath. Coroutine debugger disabled.")
|
||||||
// if debug library is included into project, add agent which installs probes
|
|
||||||
params.vmParametersList?.add("-javaagent:$it")
|
|
||||||
params.vmParametersList?.add("-ea")
|
|
||||||
val project = (configuration as RunConfigurationBase<*>).project
|
|
||||||
// add listener to put coroutines tab into debugger tab
|
|
||||||
if (project.listenerCreated != true) { // prevent multiple listeners creation
|
|
||||||
val connection = project.messageBus.connect()
|
|
||||||
connection.subscribe(
|
|
||||||
XDebuggerManager.TOPIC, createListener(project, connection)
|
|
||||||
)
|
|
||||||
project.listenerCreated = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createListener(project: Project, connection: MessageBusConnection): XDebuggerManagerListener {
|
|
||||||
return object : XDebuggerManagerListener {
|
|
||||||
override fun processStarted(debugProcess: XDebugProcess) {
|
|
||||||
DebuggerInvocationUtil.swingInvokeLater(project) {
|
|
||||||
val session = DebuggerManagerEx.getInstanceEx(project).context.debuggerSession
|
|
||||||
val ui = session?.xDebugSession?.ui
|
|
||||||
if (ui != null)
|
|
||||||
registerCoroutinesPanel(ui, session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun processStopped(debugProcess: XDebugProcess) {
|
|
||||||
connection.disconnect()
|
|
||||||
project.listenerCreated = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds panel to XDebugSessionTab
|
|
||||||
*/
|
|
||||||
private fun registerCoroutinesPanel(ui: RunnerLayoutUi, session: DebuggerSession) {
|
|
||||||
val panel = CoroutinesPanel(session.project, session.contextManager)
|
|
||||||
val content = ui.createContent(
|
|
||||||
"CoroutinesContent", panel, "Coroutines", // TODO(design)
|
|
||||||
AllIcons.Debugger.ThreadGroup, null
|
|
||||||
)
|
|
||||||
content.isCloseable = false
|
|
||||||
ui.addContent(content, 0, PlaceInGrid.left, true)
|
|
||||||
ui.addListener(object : ContentManagerAdapter() {
|
|
||||||
override fun selectionChanged(event: ContentManagerEvent) {
|
|
||||||
if (event.content === content) {
|
|
||||||
if (content.isSelected) {
|
|
||||||
panel.setUpdateEnabled(true)
|
|
||||||
if (panel.isRefreshNeeded) {
|
|
||||||
panel.rebuildIfVisible(DebuggerSession.Event.CONTEXT)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panel.setUpdateEnabled(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, content)
|
|
||||||
// add coroutine dump button: due to api problem left toolbar is copied, modified and reset to tab
|
|
||||||
val runnerContent = (ui.options as RunnerLayoutUiImpl).getData(RunnerContentUi.KEY.name) as RunnerContentUi
|
|
||||||
val modifiedActions = runnerContent.getActions(true)
|
|
||||||
val pos = modifiedActions.indexOfLast { it is ThreadDumpAction }
|
|
||||||
modifiedActions.add(pos + 1, ActionManager.getInstance().getAction("Kotlin.XDebugger.CoroutinesDump"))
|
|
||||||
ui.options.setLeftToolbar(DefaultActionGroup(modifiedActions), ActionPlaces.DEBUGGER_TOOLBAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+10
-99
@@ -4,124 +4,35 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines
|
package org.jetbrains.kotlin.idea.debugger.coroutines
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerInvocationUtil
|
|
||||||
import com.intellij.debugger.DebuggerManagerEx
|
|
||||||
import com.intellij.debugger.actions.ThreadDumpAction
|
|
||||||
import com.intellij.debugger.impl.DebuggerSession
|
|
||||||
import com.intellij.execution.RunConfigurationExtension
|
import com.intellij.execution.RunConfigurationExtension
|
||||||
import com.intellij.execution.configurations.DebuggingRunnerData
|
import com.intellij.execution.configurations.DebuggingRunnerData
|
||||||
import com.intellij.execution.configurations.JavaParameters
|
import com.intellij.execution.configurations.JavaParameters
|
||||||
import com.intellij.execution.configurations.RunConfigurationBase
|
import com.intellij.execution.configurations.RunConfigurationBase
|
||||||
import com.intellij.execution.configurations.RunnerSettings
|
import com.intellij.execution.configurations.RunnerSettings
|
||||||
import com.intellij.execution.ui.RunnerLayoutUi
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.execution.ui.layout.PlaceInGrid
|
|
||||||
import com.intellij.execution.ui.layout.impl.RunnerContentUi
|
|
||||||
import com.intellij.execution.ui.layout.impl.RunnerLayoutUiImpl
|
|
||||||
import com.intellij.icons.AllIcons
|
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
|
||||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
|
||||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.openapi.util.registry.Registry
|
|
||||||
import com.intellij.ui.content.ContentManagerAdapter
|
|
||||||
import com.intellij.ui.content.ContentManagerEvent
|
|
||||||
import com.intellij.util.messages.MessageBusConnection
|
|
||||||
import com.intellij.xdebugger.XDebugProcess
|
|
||||||
import com.intellij.xdebugger.XDebuggerManager
|
|
||||||
import com.intellij.xdebugger.XDebuggerManagerListener
|
|
||||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs coroutines debug agent and coroutines tab if `kotlinx.coroutines.debug` dependency is found
|
* Installs coroutines debug agent and coroutines tab if `kotlinx.coroutines.debug` dependency is found
|
||||||
*/
|
*/
|
||||||
@Suppress("IncompatibleAPI")
|
@Suppress("IncompatibleAPI")
|
||||||
class CoroutinesDebugConfigurationExtension : RunConfigurationExtension() {
|
class CoroutinesDebugConfigurationExtension : RunConfigurationExtension() {
|
||||||
private var Project.listenerCreated: Boolean? by UserDataProperty(Key.create("COROUTINES_DEBUG_TAB_CREATE_LISTENER"))
|
private val log = Logger.getInstance(this::class.java)
|
||||||
|
|
||||||
override fun isApplicableFor(configuration: RunConfigurationBase<*>): Boolean {
|
override fun isApplicableFor(configuration: RunConfigurationBase<*>) = isCoroutineDebuggerEnabled()
|
||||||
return Registry.`is`("kotlin.debugger.coroutines")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
|
override fun <T : RunConfigurationBase<*>?> updateJavaParameters(
|
||||||
configuration: T,
|
configuration: T,
|
||||||
params: JavaParameters,
|
params: JavaParameters,
|
||||||
runnerSettings: RunnerSettings?
|
runnerSettings: RunnerSettings?
|
||||||
) {
|
) {
|
||||||
if (!Registry.`is`("kotlin.debugger.coroutines")) return
|
if (!isCoroutineDebuggerEnabled()) return
|
||||||
if (runnerSettings is DebuggingRunnerData
|
if (runnerSettings is DebuggingRunnerData) {
|
||||||
&& params.classPath != null
|
try {
|
||||||
&& params.classPath.pathList.isNotEmpty()
|
val kotlinxCoroutinesClassPathLib = params?.classPath?.pathList?.first { it.contains("kotlinx-coroutines-debug") }
|
||||||
) {
|
initializeCoroutineAgent(params!!, kotlinxCoroutinesClassPathLib, configuration)
|
||||||
params.classPath.pathList.forEach {
|
} catch (e: NoSuchElementException) {
|
||||||
if (!it.contains("kotlinx-coroutines-debug")) return@forEach
|
log.warn("'kotlinx-coroutines-debug' not found in classpath. Coroutine debugger disabled.")
|
||||||
// if debug library is included into project, add agent which installs probes
|
|
||||||
params.vmParametersList?.add("-javaagent:$it")
|
|
||||||
params.vmParametersList?.add("-ea")
|
|
||||||
val project = (configuration as RunConfigurationBase<*>).project
|
|
||||||
// add listener to put coroutines tab into debugger tab
|
|
||||||
if (project.listenerCreated != true) { // prevent multiple listeners creation
|
|
||||||
val connection = project.messageBus.connect()
|
|
||||||
connection.subscribe(
|
|
||||||
XDebuggerManager.TOPIC, createListener(project, connection)
|
|
||||||
)
|
|
||||||
project.listenerCreated = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createListener(project: Project, connection: MessageBusConnection): XDebuggerManagerListener {
|
|
||||||
return object : XDebuggerManagerListener {
|
|
||||||
override fun processStarted(debugProcess: XDebugProcess) {
|
|
||||||
DebuggerInvocationUtil.swingInvokeLater(project) {
|
|
||||||
val session = DebuggerManagerEx.getInstanceEx(project).context.debuggerSession
|
|
||||||
val ui = session?.xDebugSession?.ui
|
|
||||||
if (ui != null)
|
|
||||||
registerCoroutinesPanel(ui, session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun processStopped(debugProcess: XDebugProcess) {
|
|
||||||
connection.disconnect()
|
|
||||||
project.listenerCreated = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds panel to XDebugSessionTab
|
|
||||||
*/
|
|
||||||
private fun registerCoroutinesPanel(ui: RunnerLayoutUi, session: DebuggerSession) {
|
|
||||||
val panel = CoroutinesPanel(session.project, session.contextManager)
|
|
||||||
val content = ui.createContent(
|
|
||||||
"CoroutinesContent", panel, "Coroutines", // TODO(design)
|
|
||||||
AllIcons.Debugger.ThreadGroup, null
|
|
||||||
)
|
|
||||||
content.isCloseable = false
|
|
||||||
ui.addContent(content, 0, PlaceInGrid.left, true)
|
|
||||||
ui.addListener(object : ContentManagerAdapter() {
|
|
||||||
override fun selectionChanged(event: ContentManagerEvent) {
|
|
||||||
if (event.content === content) {
|
|
||||||
if (content.isSelected) {
|
|
||||||
panel.setUpdateEnabled(true)
|
|
||||||
if (panel.isRefreshNeeded) {
|
|
||||||
panel.rebuildIfVisible(DebuggerSession.Event.CONTEXT)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panel.setUpdateEnabled(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, content)
|
|
||||||
// add coroutine dump button: due to api problem left toolbar is copied, modified and reset to tab
|
|
||||||
val runnerContent = (ui.options as RunnerLayoutUiImpl).getData(RunnerContentUi.KEY.name) as RunnerContentUi
|
|
||||||
val modifiedActions = runnerContent.getActions(true)
|
|
||||||
val pos = modifiedActions.indexOfLast { it is ThreadDumpAction }
|
|
||||||
modifiedActions.add(pos + 1, ActionManager.getInstance().getAction("Kotlin.XDebugger.CoroutinesDump"))
|
|
||||||
ui.options.setLeftToolbar(DefaultActionGroup(modifiedActions), ActionPlaces.DEBUGGER_TOOLBAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user