Debugger: Add breakpoint applicability tests

This commit adds a number of tests that check breakpoint placing behavior, and an inline action that work the same way as tests.
This commit is contained in:
Yan Zhulanow
2019-07-16 17:05:37 +09:00
parent a8d08815a6
commit 25fb77e7ad
19 changed files with 436 additions and 17 deletions
@@ -0,0 +1,69 @@
/*
* 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.breakpoints
import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType
import com.intellij.openapi.extensions.Extensions
import com.intellij.xdebugger.breakpoints.XBreakpointType
import com.intellij.xdebugger.breakpoints.XLineBreakpointType
import org.jetbrains.debugger.SourceInfo
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
class BreakpointChecker {
private companion object {
private val BREAKPOINT_TYPES = mapOf(
KotlinLineBreakpointType::class.java to BreakpointType.Line,
KotlinFieldBreakpointType::class.java to BreakpointType.Field,
KotlinFunctionBreakpointType::class.java to BreakpointType.Function,
JavaLineBreakpointType.LambdaJavaBreakpointVariant::class.java to BreakpointType.Lambda,
KotlinLineBreakpointType.LineKotlinBreakpointVariant::class.java to BreakpointType.Line,
KotlinLineBreakpointType.KotlinBreakpointVariant::class.java to BreakpointType.All
)
}
enum class BreakpointType(val prefix: String) {
Line("L"),
Field("F"),
Function("M"), // method
Lambda("λ"),
All("*") // line & lambda
}
@Suppress("SimplifiableCall")
private val breakpointTypes: List<XLineBreakpointType<*>> = run {
val extensionPoint = Extensions.getArea(null)
.getExtensionPoint<XBreakpointType<*, *>>(XBreakpointType.EXTENSION_POINT_NAME.name)
extensionPoint.extensions
.filterIsInstance<XLineBreakpointType<*>>()
.filter { it is KotlinBreakpointType }
}
fun check(file: KtFile, line: Int): EnumSet<BreakpointType> {
val actualBreakpointTypes = EnumSet.noneOf(BreakpointType::class.java)
for (breakpointType in breakpointTypes) {
val sign = BREAKPOINT_TYPES[breakpointType.javaClass] ?: continue
val isApplicable = breakpointType.canPutAt(file.virtualFile, line, file.project)
if (breakpointType is KotlinLineBreakpointType) {
if (isApplicable) {
val variants = breakpointType.computeVariants(file.project, SourceInfo(file.virtualFile, line))
if (variants.isNotEmpty()) {
actualBreakpointTypes += variants.mapNotNull { BREAKPOINT_TYPES[it.javaClass] }
} else {
actualBreakpointTypes += sign
}
}
} else if (isApplicable) {
actualBreakpointTypes += sign
}
}
return actualBreakpointTypes
}
}
@@ -0,0 +1,65 @@
/*
* 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.breakpoints
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.TextAnnotationGutterProvider
import com.intellij.openapi.editor.colors.ColorKey
import com.intellij.openapi.editor.colors.EditorFontType
import org.jetbrains.kotlin.idea.core.util.getLineCount
import org.jetbrains.kotlin.idea.debugger.breakpoints.BreakpointChecker.BreakpointType
import org.jetbrains.kotlin.psi.KtFile
import java.awt.Color
import java.util.*
class InspectBreakpointApplicabilityAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val data = e.getData() ?: return
if (data.editor.gutter.isAnnotationsShown) {
data.editor.gutter.closeAllAnnotations()
}
val checker = BreakpointChecker()
val lineCount = data.file.getLineCount()
val breakpoints = (0..lineCount).map { line -> checker.check(data.file, line) }
val gutterProvider = BreakpointsGutterProvider(breakpoints)
data.editor.gutter.registerTextAnnotation(gutterProvider)
}
private class BreakpointsGutterProvider(private val breakpoints: List<EnumSet<BreakpointType>>) : TextAnnotationGutterProvider {
override fun getLineText(line: Int, editor: Editor?): String? {
val breakpoints = breakpoints.getOrNull(line) ?: return null
return breakpoints.map { it.prefix }.distinct().joinToString()
}
override fun getToolTip(line: Int, editor: Editor?): String? = null
override fun getStyle(line: Int, editor: Editor?) = EditorFontType.PLAIN
override fun getPopupActions(line: Int, editor: Editor?) = emptyList<AnAction>()
override fun getColor(line: Int, editor: Editor?): ColorKey? = null
override fun getBgColor(line: Int, editor: Editor?): Color? = null
override fun gutterClosed() {}
}
override fun update(e: AnActionEvent) {
e.presentation.isVisible = ApplicationManager.getApplication().isInternal
e.presentation.isEnabled = e.getData() != null
}
class ActionData(val editor: Editor, val file: KtFile)
private fun AnActionEvent.getData(): ActionData? {
val editor = getData(CommonDataKeys.EDITOR) ?: return null
val file = getData(CommonDataKeys.PSI_FILE) as? KtFile ?: return null
return ActionData(editor, file)
}
}
@@ -45,9 +45,11 @@ import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import javax.swing.JComponent
class KotlinFieldBreakpointType : JavaBreakpointType<KotlinPropertyBreakpointProperties>, XLineBreakpointType<KotlinPropertyBreakpointProperties>(
"kotlin-field", KotlinBundle.message("debugger.field.watchpoints.tab.title")
) {
class KotlinFieldBreakpointType :
JavaBreakpointType<KotlinPropertyBreakpointProperties>,
XLineBreakpointType<KotlinPropertyBreakpointProperties>("kotlin-field", KotlinBundle.message("debugger.field.watchpoints.tab.title")),
KotlinBreakpointType
{
override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint<KotlinPropertyBreakpointProperties>): Breakpoint<KotlinPropertyBreakpointProperties> {
return KotlinFieldBreakpoint(project, breakpoint)
}
@@ -28,7 +28,10 @@ import static org.jetbrains.kotlin.idea.debugger.breakpoints.BreakpointTypeUtils
// This class is copied from com.intellij.debugger.ui.breakpoints.MethodBreakpoint.
// Changed parts are marked with '// MODIFICATION: ' comments.
// This should be deleted when IDEA opens the method breakpoint API (presumably in 193).
public class KotlinFunctionBreakpointType extends JavaLineBreakpointTypeBase<JavaMethodBreakpointProperties> {
public class KotlinFunctionBreakpointType
extends JavaLineBreakpointTypeBase<JavaMethodBreakpointProperties>
implements KotlinBreakpointType
{
// MODIFICATION: Start Kotlin implementation
public KotlinFunctionBreakpointType() {
super("kotlin-function", KotlinBundle.message("debugger.function.breakpoints.tab.title"));
@@ -51,7 +51,7 @@ import java.util.List;
import static org.jetbrains.kotlin.idea.debugger.breakpoints.BreakpointTypeUtilsKt.isBreakpointApplicable;
public class KotlinLineBreakpointType extends JavaLineBreakpointType {
public class KotlinLineBreakpointType extends JavaLineBreakpointType implements KotlinBreakpointType {
public KotlinLineBreakpointType() {
super("kotlin-line", KotlinBundle.message("debugger.line.breakpoints.tab.title"));
}
@@ -39,6 +39,8 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.util.*
interface KotlinBreakpointType
class ApplicabilityResult(val isApplicable: Boolean, val shouldStop: Boolean) {
companion object {
@JvmStatic