Smart step into function literal
This commit is contained in:
@@ -228,7 +228,7 @@ public class JetPositionManager(private val myDebugProcess: DebugProcess) : Mult
|
||||
}
|
||||
|
||||
public fun classNameForPosition(sourcePosition: SourcePosition): String? {
|
||||
val psiElement = sourcePosition.getElementAt()
|
||||
val psiElement = runReadAction { sourcePosition.getElementAt() }
|
||||
if (psiElement == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.stepping
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.BreakpointStepMethodFilter
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.LambdaMethodFilter
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.util.Range
|
||||
import com.sun.jdi.Location
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
||||
import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
public class KotlinLambdaMethodFilter(
|
||||
lambda: JetFunctionLiteralExpression,
|
||||
private val myCallingExpressionLines: Range<Int>
|
||||
): BreakpointStepMethodFilter {
|
||||
private val myFirstStatementPosition: SourcePosition?
|
||||
private val myLastStatementLine: Int
|
||||
|
||||
init {
|
||||
var firstStatementPosition: SourcePosition? = null
|
||||
var lastStatementPosition: SourcePosition? = null
|
||||
val body = lambda.getBodyExpression()!!
|
||||
val statements = body.getStatements()
|
||||
if (statements.isNotEmpty()) {
|
||||
firstStatementPosition = SourcePosition.createFromElement(statements.first())
|
||||
if (firstStatementPosition != null) {
|
||||
val lastStatement = statements.last()
|
||||
lastStatementPosition = SourcePosition.createFromOffset(firstStatementPosition.getFile(), lastStatement.getTextRange().getEndOffset())
|
||||
}
|
||||
}
|
||||
myFirstStatementPosition = firstStatementPosition
|
||||
myLastStatementLine = if (lastStatementPosition != null) lastStatementPosition.getLine() else -1
|
||||
}
|
||||
|
||||
override fun getBreakpointPosition(): SourcePosition? {
|
||||
return myFirstStatementPosition
|
||||
}
|
||||
|
||||
override fun getLastStatementLine(): Int {
|
||||
return myLastStatementLine
|
||||
}
|
||||
|
||||
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {
|
||||
val method = location.method()
|
||||
return isLambdaName(method.name())
|
||||
}
|
||||
|
||||
override fun getCallingExpressionLines(): Range<Int>? {
|
||||
return myCallingExpressionLines
|
||||
}
|
||||
|
||||
companion object {
|
||||
public fun isLambdaName(name: String?): Boolean {
|
||||
return name == OperatorConventions.INVOKE.asString()
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.stepping
|
||||
|
||||
import com.intellij.debugger.actions.MethodSmartStepTarget
|
||||
import com.intellij.debugger.actions.SmartStepTarget
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.util.Range
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetIcons
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import javax.swing.Icon
|
||||
|
||||
public class KotlinLambdaSmartStepTarget(
|
||||
label: String,
|
||||
highlightElement: JetFunctionLiteralExpression,
|
||||
lines: Range<Int>
|
||||
): SmartStepTarget(label, highlightElement, true, lines) {
|
||||
override fun getIcon() = JetIcons.LAMBDA
|
||||
|
||||
fun getLambda() = getHighlightElement() as JetFunctionLiteralExpression
|
||||
|
||||
companion object {
|
||||
fun calcLabel(descriptor: DeclarationDescriptor, paramName: Name): String {
|
||||
return "${descriptor.getName().asString()}: ${paramName.asString()}.${OperatorConventions.INVOKE.asString()}()"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,25 +20,23 @@ import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.actions.JvmSmartStepIntoHandler
|
||||
import com.intellij.debugger.actions.MethodSmartStepTarget
|
||||
import com.intellij.debugger.actions.SmartStepTarget
|
||||
import com.intellij.debugger.engine.BasicStepMethodFilter
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.MethodFilter
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.util.Range
|
||||
import com.intellij.util.containers.OrderedSet
|
||||
import com.sun.jdi.Location
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.debugger.MockSourcePosition
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
|
||||
public class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
|
||||
@@ -68,7 +66,18 @@ public class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
element.accept(object: JetTreeVisitorVoid() {
|
||||
|
||||
override fun visitFunctionLiteralExpression(expression: JetFunctionLiteralExpression) {
|
||||
// skip calls in function literals
|
||||
val context = expression.analyzeAndGetResult().bindingContext
|
||||
val resolvedCall = expression.getParentCall(context).getResolvedCall(context)
|
||||
if (resolvedCall != null && !InlineUtil.isInline(resolvedCall.getResultingDescriptor())) {
|
||||
val arguments = resolvedCall.getValueArguments()
|
||||
for ((param, argument) in arguments) {
|
||||
if (argument.getArguments().any { it.getArgumentExpression() == expression}) {
|
||||
val label = KotlinLambdaSmartStepTarget.calcLabel(resolvedCall.getResultingDescriptor(), param.getName())
|
||||
result.add(KotlinLambdaSmartStepTarget(label, expression, lines))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitObjectLiteralExpression(expression: JetObjectLiteralExpression) {
|
||||
@@ -177,9 +186,10 @@ public class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
}
|
||||
|
||||
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
|
||||
if (stepTarget is KotlinMethodSmartStepTarget) {
|
||||
return KotlinBasicStepMethodFilter(stepTarget)
|
||||
return when (stepTarget) {
|
||||
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget)
|
||||
is KotlinLambdaSmartStepTarget -> KotlinLambdaMethodFilter(stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!! )
|
||||
else -> super.createMethodFilter(stepTarget)
|
||||
}
|
||||
return super.createMethodFilter(stepTarget)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,4 +7,4 @@ fun foo() {
|
||||
fun f1(f: () -> Unit) {}
|
||||
fun f2() {}
|
||||
|
||||
// EXISTS: f1(Function0<? extends Unit>)
|
||||
// EXISTS: f1(Function0<? extends Unit>), f1: f.invoke()
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo() {
|
||||
<caret>f1() { }
|
||||
}
|
||||
|
||||
inline fun f1(f: () -> Unit) {}
|
||||
|
||||
// EXISTS: f1(Function0<? extends Unit>)
|
||||
@@ -23,6 +23,7 @@ import com.intellij.psi.util.PsiFormatUtil
|
||||
import com.intellij.psi.util.PsiFormatUtilBase
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinLambdaSmartStepTarget
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSmartStepIntoHandler
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
@@ -40,7 +41,7 @@ public abstract class AbstractSmartStepIntoTest : JetLightCodeInsightFixtureTest
|
||||
|
||||
val position = MockSourcePosition(_file = fixture.getFile(), _line = line, _offset = offset, _editor = fixture.getEditor())
|
||||
|
||||
val actual = KotlinSmartStepIntoHandler().findSmartStepTargets(position).map { renderTarget(it) }
|
||||
val actual = KotlinSmartStepIntoHandler().findSmartStepTargets(position).map { it.getPresentation() }
|
||||
|
||||
val expected = InTextDirectivesUtils.findListWithPrefixes(fixture.getFile()?.getText()!!.replace("\\,", "+++"), "// EXISTS: ").map { it.replace("+++", ",") }
|
||||
|
||||
@@ -73,30 +74,6 @@ public abstract class AbstractSmartStepIntoTest : JetLightCodeInsightFixtureTest
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun renderTarget(target: SmartStepTarget): String {
|
||||
val sb = StringBuilder()
|
||||
|
||||
val label = target.getLabel()
|
||||
if (label != null) {
|
||||
sb.append(label)
|
||||
}
|
||||
when (target) {
|
||||
is MethodSmartStepTarget -> {
|
||||
sb.append(PsiFormatUtil.formatMethod(
|
||||
target.getMethod(),
|
||||
PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
PsiFormatUtilBase.SHOW_TYPE,
|
||||
999
|
||||
))
|
||||
}
|
||||
else -> {
|
||||
sb.append("Renderer for ${target.javaClass} should be implemented")
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String? {
|
||||
return PluginTestCaseBase.getTestDataPathBase() + "/debugger/smartStepInto"
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ public class SmartStepIntoTestGenerated extends AbstractSmartStepIntoTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlinedFunLiteral.kt")
|
||||
public void testInlinedFunLiteral() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/smartStepInto/inlinedFunLiteral.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invoke.kt")
|
||||
public void testInvoke() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/smartStepInto/invoke.kt");
|
||||
|
||||
Reference in New Issue
Block a user