diff --git a/annotations/com/intellij/debugger/actions/annotations.xml b/annotations/com/intellij/debugger/actions/annotations.xml
new file mode 100644
index 00000000000..b0e4938b1d5
--- /dev/null
+++ b/annotations/com/intellij/debugger/actions/annotations.xml
@@ -0,0 +1,12 @@
+
+ -
+
+
+
+
+ -
+
+
+
diff --git a/annotations/com/intellij/util/annotations.xml b/annotations/com/intellij/util/annotations.xml
index b8e4ab96ac5..b6951be234c 100644
--- a/annotations/com/intellij/util/annotations.xml
+++ b/annotations/com/intellij/util/annotations.xml
@@ -8,4 +8,10 @@
-
+ -
+
+
+ -
+
+
\ No newline at end of file
diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt
index c25e47f8fff..b4bd920e8a5 100644
--- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt
+++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt
@@ -99,6 +99,7 @@ import org.jetbrains.jet.plugin.refactoring.move.AbstractJetMoveTest
import org.jetbrains.jet.cfg.AbstractDataFlowTest
import org.jetbrains.jet.plugin.libraries.AbstractDecompiledTextTest
import org.jetbrains.jet.plugin.imports.AbstractOptimizeImportsTest
+import org.jetbrains.jet.plugin.debugger.AbstractSmartStepIntoTest
fun main(args: Array) {
System.setProperty("java.awt.headless", "true")
@@ -524,6 +525,10 @@ fun main(args: Array) {
testClass(javaClass()) {
model("editor/optimizeImports", extension = null, recursive = false)
}
+
+ testClass(javaClass()) {
+ model("debugger/smartStepInto")
+ }
}
testGroup("j2k/tests/test", "j2k/tests/testData") {
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 459b79600ba..196efef5488 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -299,6 +299,7 @@
id="kotlinJavaSafeDeleteDelegate"
language="jet"
implementationClass="org.jetbrains.jet.plugin.refactoring.safeDelete.KotlinJavaSafeDeleteDelegate"/>
+
diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSmartStepIntoHandler.kt b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSmartStepIntoHandler.kt
new file mode 100644
index 00000000000..893285f977d
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSmartStepIntoHandler.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2010-2014 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.jet.plugin.debugger
+
+import com.intellij.debugger.actions.JvmSmartStepIntoHandler
+import com.intellij.debugger.SourcePosition
+import com.intellij.debugger.actions.SmartStepTarget
+import java.util.Collections
+import com.intellij.openapi.fileEditor.FileDocumentManager
+import com.intellij.debugger.actions.MethodSmartStepTarget
+import com.intellij.util.containers.OrderedSet
+import com.intellij.util.Range
+import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
+import org.jetbrains.jet.asJava.LightClassUtil
+import org.jetbrains.jet.lang.resolve.BindingContextUtils
+import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
+import com.intellij.psi.PsiElement
+import com.intellij.util.text.CharArrayUtil
+import org.jetbrains.jet.lang.psi.*
+import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
+
+public class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
+
+ override fun isAvailable(position: SourcePosition?) = position?.getFile() is JetFile
+
+ override fun findSmartStepTargets(position: SourcePosition): List {
+ if (position.getLine() < 0) return Collections.emptyList()
+
+ val file = position.getFile()
+ val vFile = file.getVirtualFile()
+ if (vFile == null) return Collections.emptyList()
+
+ val doc = FileDocumentManager.getInstance()?.getDocument(vFile)
+ if (doc == null) return Collections.emptyList()
+
+ val line = position.getLine()
+ if (line >= doc.getLineCount()) return Collections.emptyList()
+
+ val lineStartOffset = doc.getLineStartOffset(line)
+ val offsetWithoutTab = CharArrayUtil.shiftForward(doc.getCharsSequence(), lineStartOffset, " \t")
+ val elementAtOffset = file.findElementAt(offsetWithoutTab)
+ if (elementAtOffset == null) return Collections.emptyList()
+
+ val element = getTopmostElementAtOffset(elementAtOffset, lineStartOffset)
+
+ if (element !is JetElement) return Collections.emptyList()
+
+ val elementTextRange = element.getTextRange()
+ if (elementTextRange == null) return Collections.emptyList()
+
+ val lines = Range(doc.getLineNumber(elementTextRange.getStartOffset()), doc.getLineNumber(elementTextRange.getEndOffset()))
+ val bindingContext = AnalyzerFacadeWithCache.getContextForElement(element)
+ val result = OrderedSet()
+
+ // TODO support class initializers, local functions, delegated properties with specified type, setter for properties
+ element.accept(object: JetTreeVisitorVoid() {
+
+ override fun visitFunctionLiteralExpression(expression: JetFunctionLiteralExpression) {
+ // skip calls in function literals
+ }
+
+ override fun visitObjectLiteralExpression(expression: JetObjectLiteralExpression) {
+ // skip calls in object declarations
+ }
+
+ override fun visitIfExpression(expression: JetIfExpression) {
+ expression.getCondition()?.accept(this)
+ }
+
+ override fun visitWhileExpression(expression: JetWhileExpression) {
+ expression.getCondition()?.accept(this)
+ }
+
+ override fun visitDoWhileExpression(expression: JetDoWhileExpression) {
+ expression.getCondition()?.accept(this)
+ }
+
+ override fun visitForExpression(expression: JetForExpression) {
+ expression.getLoopRange()?.accept(this)
+ }
+
+ override fun visitWhenExpression(expression: JetWhenExpression) {
+ expression.getSubjectExpression()?.accept(this)
+ }
+
+ override fun visitArrayAccessExpression(expression: JetArrayAccessExpression) {
+ recordFunction(expression)
+ super.visitArrayAccessExpression(expression)
+ }
+
+ override fun visitUnaryExpression(expression: JetUnaryExpression) {
+ recordFunction(expression.getOperationReference())
+ super.visitUnaryExpression(expression)
+ }
+
+ override fun visitBinaryExpression(expression: JetBinaryExpression) {
+ recordFunction(expression.getOperationReference())
+ super.visitBinaryExpression(expression)
+ }
+
+ override fun visitCallExpression(expression: JetCallExpression) {
+ val calleeExpression = expression.getCalleeExpression()
+ if (calleeExpression != null) {
+ recordFunction(calleeExpression)
+ }
+ super.visitCallExpression(expression)
+ }
+
+ override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
+ val resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, expression)
+ if (resolvedCall != null) {
+ val propertyDescriptor = resolvedCall.getResultingDescriptor()
+ if (propertyDescriptor is PropertyDescriptor) {
+ val getterDescriptor = propertyDescriptor.getGetter()
+ if (getterDescriptor != null && !getterDescriptor.isDefault()) {
+ val delegatedResolvedCall = bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor)
+ if (delegatedResolvedCall == null) {
+ val getter = BindingContextUtils.callableDescriptorToDeclaration(bindingContext, getterDescriptor)
+ if (getter is JetPropertyAccessor && (getter.getBodyExpression() != null || getter.getEqualsToken() != null)) {
+ val psiMethod = LightClassUtil.getLightClassAccessorMethod(getter)
+ if (psiMethod != null) {
+ result.add(MethodSmartStepTarget(psiMethod, null, expression, false, lines))
+ }
+ }
+ }
+ else {
+ val delegatedPropertyGetterDescriptor = delegatedResolvedCall.getResultingDescriptor()
+ if (delegatedPropertyGetterDescriptor is CallableMemberDescriptor) {
+ val function = BindingContextUtils.callableDescriptorToDeclaration(bindingContext, delegatedPropertyGetterDescriptor)
+ if (function is JetNamedFunction) {
+ val psiMethod = LightClassUtil.getLightClassMethod(function)
+ if (psiMethod != null) {
+ result.add(MethodSmartStepTarget(psiMethod, "${propertyDescriptor.getName()}.", expression, false, lines))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ super.visitSimpleNameExpression(expression)
+ }
+
+ private fun recordFunction(expression: JetExpression) {
+ val resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, expression)
+ if (resolvedCall == null) return
+
+ val descriptor = resolvedCall.getResultingDescriptor()
+ if (descriptor is CallableMemberDescriptor) {
+ val function = BindingContextUtils.callableDescriptorToDeclaration(bindingContext, descriptor)
+ if (function is JetNamedFunction) {
+ val psiMethod = LightClassUtil.getLightClassMethod(function)
+ if (psiMethod != null) {
+ result.add(MethodSmartStepTarget(method = psiMethod, label = null,
+ highlightElement = expression,
+ needBreakpointRequest = false, lines = lines))
+ }
+ }
+ }
+ }
+ }, null)
+
+ return result
+ }
+
+ private fun getTopmostElementAtOffset(element: PsiElement, offset: Int): PsiElement? {
+ var resultElement: PsiElement? = element
+ while (resultElement?.getParent()?.getTextRange() != null &&
+ resultElement?.getParent()?.getTextRange()!!.getStartOffset() >= offset) {
+ resultElement = resultElement!!.getParent()
+ }
+
+ return resultElement
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/annotation.kt b/idea/testData/debugger/smartStepInto/annotation.kt
new file mode 100644
index 00000000000..1ad6b6aa118
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/annotation.kt
@@ -0,0 +1,9 @@
+fun foo() {
+ [Ann() Ann] val a = bar()
+}
+
+annotation class Ann
+
+fun bar() = 1
+
+// EXISTS: bar()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/arrayAccess.kt b/idea/testData/debugger/smartStepInto/arrayAccess.kt
new file mode 100644
index 00000000000..d45350bbb9c
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/arrayAccess.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ val a = A()
+ a[1]
+}
+
+class A {
+ fun get(i: Int) = 1
+}
+
+// EXISTS: get(int)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/callChain.kt b/idea/testData/debugger/smartStepInto/callChain.kt
new file mode 100644
index 00000000000..3ef9f69afee
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/callChain.kt
@@ -0,0 +1,13 @@
+fun foo() {
+ A().getB().f1()
+}
+
+class A {
+ fun getB() = B()
+}
+
+class B {
+ fun f1() {}
+}
+
+// EXISTS: getB(), f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/conventionMethod.kt b/idea/testData/debugger/smartStepInto/conventionMethod.kt
new file mode 100644
index 00000000000..ce349621e2f
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/conventionMethod.kt
@@ -0,0 +1,11 @@
+class A {
+ fun plus(a: A) {}
+}
+
+fun foo() {
+ f1() + A() + A()
+}
+
+fun f1() = A()
+
+// EXISTS: plus(A), f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/delegatedPropertyGetter.kt b/idea/testData/debugger/smartStepInto/delegatedPropertyGetter.kt
new file mode 100644
index 00000000000..61996cb63f3
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/delegatedPropertyGetter.kt
@@ -0,0 +1,11 @@
+fun foo() {
+ a
+}
+
+val a by Delegate()
+
+class Delegate {
+ fun get(t: Any?, p: PropertyMetadata) = 1
+}
+
+// EXISTS: a.get(Object\, PropertyMetadata)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/doWhile.kt b/idea/testData/debugger/smartStepInto/doWhile.kt
new file mode 100644
index 00000000000..e5178aaf013
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/doWhile.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ do {
+ f2()
+ } while (f1())
+}
+
+fun f1() = true
+fun f2() {}
+
+// EXISTS: f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/dotQualified.kt b/idea/testData/debugger/smartStepInto/dotQualified.kt
new file mode 100644
index 00000000000..7f07291a5af
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/dotQualified.kt
@@ -0,0 +1,12 @@
+fun foo() {
+ val a = A()
+ a.f1(f2())
+}
+
+class A {
+ fun f1(): Int = 1
+}
+
+fun f2() {}
+
+// EXISTS: f1(), f2()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/dotQualifiedInParam.kt b/idea/testData/debugger/smartStepInto/dotQualifiedInParam.kt
new file mode 100644
index 00000000000..57471ec6a85
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/dotQualifiedInParam.kt
@@ -0,0 +1,12 @@
+fun foo() {
+ val a = A()
+ f2(a.f1())
+}
+
+class A {
+ fun f1() = 1
+}
+
+fun f2(i: Int) {}
+
+// EXISTS: f1(), f2(int)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/empty.kt b/idea/testData/debugger/smartStepInto/empty.kt
new file mode 100644
index 00000000000..672b9e305ba
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/empty.kt
@@ -0,0 +1,3 @@
+fun foo() {
+ val a = 1
+}
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/for.kt b/idea/testData/debugger/smartStepInto/for.kt
new file mode 100644
index 00000000000..30edaa79991
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/for.kt
@@ -0,0 +1,11 @@
+fun foo() {
+ val a = 1
+ for (a in f1()) {
+ f2()
+ }
+}
+
+fun f1() = 1..2
+fun f2() {}
+
+// EXISTS: f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/funLiteral.kt b/idea/testData/debugger/smartStepInto/funLiteral.kt
new file mode 100644
index 00000000000..f17391eda06
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/funLiteral.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ f1() {
+ f2()
+ }
+}
+
+fun f1(f: () -> Unit) {}
+fun f2() {}
+
+// EXISTS: f1(Function0 extends Unit>)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/if.kt b/idea/testData/debugger/smartStepInto/if.kt
new file mode 100644
index 00000000000..f47defc3c72
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/if.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ if (f1()) {
+ f2()
+ }
+}
+
+fun f1() = true
+fun f2() {}
+
+// EXISTS: f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/infixCall.kt b/idea/testData/debugger/smartStepInto/infixCall.kt
new file mode 100644
index 00000000000..3e12514eb58
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/infixCall.kt
@@ -0,0 +1,12 @@
+fun foo() {
+ val a = A()
+ f2(a f1 1)
+}
+
+class A {
+ fun f1(i: Int) = 1
+}
+
+fun f2(i: Int) {}
+
+// EXISTS: f1(int), f2(int)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/invoke.kt b/idea/testData/debugger/smartStepInto/invoke.kt
new file mode 100644
index 00000000000..0c836c2efe0
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/invoke.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ val a = A()
+ a()
+}
+
+class A {
+ fun invoke() {}
+}
+
+// EXISTS: invoke()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/multiline.kt b/idea/testData/debugger/smartStepInto/multiline.kt
new file mode 100644
index 00000000000..8091a6162d9
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/multiline.kt
@@ -0,0 +1,12 @@
+fun foo() {
+ f1(
+ f2(),
+ f3()
+ )
+}
+
+fun f1(vararg i: Int) {}
+fun f2() = 1
+fun f3() = 1
+
+// EXISTS: f1(int...), f2(), f3()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/multilineCallChain.kt b/idea/testData/debugger/smartStepInto/multilineCallChain.kt
new file mode 100644
index 00000000000..605998a1bd4
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/multilineCallChain.kt
@@ -0,0 +1,15 @@
+fun foo() {
+ A()
+ .getB()
+ .f1()
+}
+
+class A {
+ fun getB() = B()
+}
+
+class B {
+ fun f1() {}
+}
+
+// EXISTS: getB(), f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/object.kt b/idea/testData/debugger/smartStepInto/object.kt
new file mode 100644
index 00000000000..a020d4286c0
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/object.kt
@@ -0,0 +1,9 @@
+fun foo() {
+ val a = object {
+ fun f() {
+ f2()
+ }
+ }
+}
+
+fun f2() {}
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/param.kt b/idea/testData/debugger/smartStepInto/param.kt
new file mode 100644
index 00000000000..222ad3d7a0e
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/param.kt
@@ -0,0 +1,8 @@
+fun foo() {
+ f1(f2())
+}
+
+fun f1(i: Int) = 1
+fun f2() {}
+
+// EXISTS: f1(int), f2()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/parantesized.kt b/idea/testData/debugger/smartStepInto/parantesized.kt
new file mode 100644
index 00000000000..b3522e22073
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/parantesized.kt
@@ -0,0 +1,7 @@
+fun foo() {
+ (bar())
+}
+
+fun bar() {}
+
+// EXISTS: bar()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/propertyGetter.kt b/idea/testData/debugger/smartStepInto/propertyGetter.kt
new file mode 100644
index 00000000000..a836dee2a5d
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/propertyGetter.kt
@@ -0,0 +1,18 @@
+fun foo() {
+ a + b + c + d
+}
+
+val a = 1
+
+val b = 1
+ get
+
+val c: Int
+ get() = 1
+
+val d: Int
+ get() {
+ return 1
+ }
+
+// EXISTS: getC(), getD()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/simple.kt b/idea/testData/debugger/smartStepInto/simple.kt
new file mode 100644
index 00000000000..6db3a601e0e
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/simple.kt
@@ -0,0 +1,7 @@
+fun foo() {
+ bar()
+}
+
+fun bar() {}
+
+// EXISTS: bar()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/stringTemplate.kt b/idea/testData/debugger/smartStepInto/stringTemplate.kt
new file mode 100644
index 00000000000..c5a5916ad3b
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/stringTemplate.kt
@@ -0,0 +1,8 @@
+fun foo() {
+ f2("aaa${f1()}")
+}
+
+fun f1() = "1"
+fun f2(s: String) {}
+
+// EXISTS: f1(), f2(String)
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/unary.kt b/idea/testData/debugger/smartStepInto/unary.kt
new file mode 100644
index 00000000000..34b4f9af166
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/unary.kt
@@ -0,0 +1,9 @@
+class A {
+ fun plus() {}
+ fun minus() {}
+}
+
+fun foo() {
+ +A() || A()-
+}
+// EXISTS: plus(), minus()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/when.kt b/idea/testData/debugger/smartStepInto/when.kt
new file mode 100644
index 00000000000..1b783f4ae81
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/when.kt
@@ -0,0 +1,13 @@
+fun foo() {
+ when (f1()) {
+ true -> f2()
+ else -> {
+ f2()
+ }
+ }
+}
+
+fun f1() = true
+fun f2() {}
+
+// EXISTS: f1()
\ No newline at end of file
diff --git a/idea/testData/debugger/smartStepInto/while.kt b/idea/testData/debugger/smartStepInto/while.kt
new file mode 100644
index 00000000000..0e4ff9ef4d7
--- /dev/null
+++ b/idea/testData/debugger/smartStepInto/while.kt
@@ -0,0 +1,10 @@
+fun foo() {
+ while (f1()) {
+ f2()
+ }
+}
+
+fun f1() = true
+fun f2() {}
+
+// EXISTS: f1()
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/AbstractSmartStepIntoTest.kt b/idea/tests/org/jetbrains/jet/plugin/debugger/AbstractSmartStepIntoTest.kt
new file mode 100644
index 00000000000..3ded7b3e39f
--- /dev/null
+++ b/idea/tests/org/jetbrains/jet/plugin/debugger/AbstractSmartStepIntoTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2010-2014 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.jet.plugin.debugger
+
+import com.intellij.debugger.SourcePosition
+import com.intellij.psi.PsiElement
+import com.intellij.debugger.actions.SmartStepTarget
+import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase
+import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
+import org.jetbrains.jet.plugin.PluginTestCaseBase
+import com.intellij.debugger.actions.MethodSmartStepTarget
+import org.jetbrains.jet.InTextDirectivesUtils
+import com.intellij.psi.util.PsiFormatUtil
+import com.intellij.psi.PsiSubstitutor
+import com.intellij.psi.util.PsiFormatUtilBase
+
+abstract class AbstractSmartStepIntoTest : JetLightCodeInsightFixtureTestCase() {
+ private val fixture: JavaCodeInsightTestFixture
+ get() = myFixture!!
+
+ protected fun doTest(path: String) {
+ fixture.configureByFile(path)
+
+ val offset = fixture.getCaretOffset()
+ val line = fixture.getDocument(fixture.getFile())!!.getLineNumber(offset)
+
+ val position = object: SourcePosition() {
+ override fun getFile() = fixture.getFile()!!
+ override fun getElementAt(): PsiElement? = throw UnsupportedOperationException()
+ override fun getLine() = line
+ override fun getOffset() = offset
+ override fun openEditor(requestFocus: Boolean) = fixture.getEditor()
+ override fun navigate(requestFocus: Boolean) {
+ }
+ override fun canNavigate() = false
+ override fun canNavigateToSource() = false
+ }
+
+ val actual = KotlinSmartStepIntoHandler().findSmartStepTargets(position).map { renderTarget(it) }
+
+ val expected = InTextDirectivesUtils.findListWithPrefixes(fixture.getFile()?.getText()!!.replace("\\,", "+++"), "// EXISTS: ").map { it.replace("+++", ",") }
+
+ for (actualTargetName in actual) {
+ assert(expected.contains(actualTargetName), "Unexpected step into target was found: ${actualTargetName}\n${renderTableWithResults(expected, actual)}")
+ }
+
+ for (expectedTargetName in expected) {
+ assert(actual.contains(expectedTargetName), "Missed step into target: ${expectedTargetName}\n${renderTableWithResults(expected, actual)}")
+ }
+ }
+
+ private fun renderTableWithResults(expected: List, actual: List): String {
+ val sb = StringBuilder()
+
+ val maxExtStrSize = (expected.maxBy { it.size }?.size ?: 0) + 5
+ val longerList = if (expected.size < actual.size) actual else expected
+ val shorterList = if (expected.size < actual.size) expected else actual
+ for ((i, element) in longerList.withIndices()) {
+ sb.append(element)
+ sb.append(" ".repeat(maxExtStrSize - element.size))
+ if (i < shorterList.size) sb.append(shorterList[i])
+ sb.append("\n")
+ }
+
+ 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"
+ }
+}
diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/SmartStepIntoTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/debugger/SmartStepIntoTestGenerated.java
new file mode 100644
index 00000000000..dddae58b916
--- /dev/null
+++ b/idea/tests/org/jetbrains/jet/plugin/debugger/SmartStepIntoTestGenerated.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2010-2014 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.jet.plugin.debugger;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import java.io.File;
+import java.util.regex.Pattern;
+import org.jetbrains.jet.JetTestUtils;
+import org.jetbrains.jet.test.InnerTestClasses;
+import org.jetbrains.jet.test.TestMetadata;
+
+import org.jetbrains.jet.plugin.debugger.AbstractSmartStepIntoTest;
+
+/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("idea/testData/debugger/smartStepInto")
+public class SmartStepIntoTestGenerated extends AbstractSmartStepIntoTest {
+ public void testAllFilesPresentInSmartStepInto() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/debugger/smartStepInto"), Pattern.compile("^(.+)\\.kt$"), true);
+ }
+
+ @TestMetadata("annotation.kt")
+ public void testAnnotation() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/annotation.kt");
+ }
+
+ @TestMetadata("arrayAccess.kt")
+ public void testArrayAccess() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/arrayAccess.kt");
+ }
+
+ @TestMetadata("callChain.kt")
+ public void testCallChain() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/callChain.kt");
+ }
+
+ @TestMetadata("conventionMethod.kt")
+ public void testConventionMethod() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/conventionMethod.kt");
+ }
+
+ @TestMetadata("delegatedPropertyGetter.kt")
+ public void testDelegatedPropertyGetter() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/delegatedPropertyGetter.kt");
+ }
+
+ @TestMetadata("doWhile.kt")
+ public void testDoWhile() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/doWhile.kt");
+ }
+
+ @TestMetadata("dotQualified.kt")
+ public void testDotQualified() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/dotQualified.kt");
+ }
+
+ @TestMetadata("dotQualifiedInParam.kt")
+ public void testDotQualifiedInParam() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/dotQualifiedInParam.kt");
+ }
+
+ @TestMetadata("empty.kt")
+ public void testEmpty() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/empty.kt");
+ }
+
+ @TestMetadata("for.kt")
+ public void testFor() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/for.kt");
+ }
+
+ @TestMetadata("funLiteral.kt")
+ public void testFunLiteral() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/funLiteral.kt");
+ }
+
+ @TestMetadata("if.kt")
+ public void testIf() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/if.kt");
+ }
+
+ @TestMetadata("infixCall.kt")
+ public void testInfixCall() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/infixCall.kt");
+ }
+
+ @TestMetadata("invoke.kt")
+ public void testInvoke() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/invoke.kt");
+ }
+
+ @TestMetadata("multiline.kt")
+ public void testMultiline() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/multiline.kt");
+ }
+
+ @TestMetadata("multilineCallChain.kt")
+ public void testMultilineCallChain() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/multilineCallChain.kt");
+ }
+
+ @TestMetadata("object.kt")
+ public void testObject() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/object.kt");
+ }
+
+ @TestMetadata("param.kt")
+ public void testParam() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/param.kt");
+ }
+
+ @TestMetadata("parantesized.kt")
+ public void testParantesized() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/parantesized.kt");
+ }
+
+ @TestMetadata("propertyGetter.kt")
+ public void testPropertyGetter() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/propertyGetter.kt");
+ }
+
+ @TestMetadata("simple.kt")
+ public void testSimple() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/simple.kt");
+ }
+
+ @TestMetadata("stringTemplate.kt")
+ public void testStringTemplate() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/stringTemplate.kt");
+ }
+
+ @TestMetadata("unary.kt")
+ public void testUnary() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/unary.kt");
+ }
+
+ @TestMetadata("when.kt")
+ public void testWhen() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/when.kt");
+ }
+
+ @TestMetadata("while.kt")
+ public void testWhile() throws Exception {
+ doTest("idea/testData/debugger/smartStepInto/while.kt");
+ }
+
+}