Debugger: implement findExpression method correctly to make work Alt+Click in debugger

This commit is contained in:
Natalia Ukhorskaya
2014-04-11 12:42:38 +04:00
parent bd737a32fb
commit 45d78dae5a
28 changed files with 404 additions and 6 deletions
@@ -106,6 +106,7 @@ import org.jetbrains.jet.plugin.debugger.AbstractKotlinSteppingTest
import org.jetbrains.jet.completion.AbstractMultiFileJvmBasicCompletionTest
import org.jetbrains.jet.plugin.refactoring.introduce.introduceVariable.AbstractJetExtractionTest
import org.jetbrains.jet.plugin.debugger.evaluate.AbstractKotlinEvaluateExpressionTest
import org.jetbrains.jet.plugin.debugger.evaluate.AbstractSelectExpressionForDebuggerTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
@@ -574,6 +575,10 @@ fun main(args: Array<String>) {
model("refactoring/introduceVariable", extension = "kt", testMethod = "doIntroduceVariableTest")
model("refactoring/extractFunction", extension = "kt", testMethod = "doExtractFunctionTest")
}
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
model("debugger/selectExpression")
}
}
testGroup("j2k/tests/test", "j2k/tests/testData") {
@@ -23,18 +23,29 @@ import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.Pair
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
import com.intellij.openapi.fileTypes.FileType
import org.jetbrains.jet.plugin.JetFileType
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.lang.psi.JetReferenceExpression
import org.jetbrains.jet.lang.psi.JetThisExpression
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.psi.JetOperationExpression
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetExpressionCodeFragmentImpl
import org.jetbrains.jet.lang.psi.JetSuperExpression
class KotlinEditorTextProvider : EditorTextProvider {
override fun getEditorText(elementAtCaret: PsiElement): TextWithImports? {
return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, elementAtCaret.getText(), getImports(elementAtCaret), JetFileType.INSTANCE)
val expression = findExpressionInner(elementAtCaret)
return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression?.getText() ?: "", getImports(elementAtCaret), JetFileType.INSTANCE)
}
override fun findExpression(elementAtCaret: PsiElement, allowMethodCalls: Boolean): Pair<PsiElement, TextRange>? {
return Pair(elementAtCaret, elementAtCaret.getTextRange())
val expression = findExpressionInner(elementAtCaret)
if (expression == null) return null
return Pair(expression, expression.getTextRange())
}
class object {
@@ -43,8 +54,51 @@ class KotlinEditorTextProvider : EditorTextProvider {
if (containingFile !is JetFile) return ""
return containingFile.getImportList()?.getImports()
?.map { it.getText() }
?.makeString(JetExpressionCodeFragmentImpl.IMPORT_SEPARATOR) ?: ""
?.map { it.getText() }
?.makeString(JetExpressionCodeFragmentImpl.IMPORT_SEPARATOR) ?: ""
}
fun findExpressionInner(element: PsiElement): JetExpression? {
val jetElement = PsiTreeUtil.getParentOfType(element, javaClass<JetElement>())
if (jetElement == null) return null
val parent = jetElement.getParent()
if (parent == null) return null
val newExpression = when (parent) {
is JetThisExpression,
is JetSuperExpression,
is JetReferenceExpression -> {
val pparent = parent.getParent()
when (pparent) {
is JetQualifiedExpression -> pparent
else -> parent
}
}
is JetQualifiedExpression -> {
if (parent.getReceiverExpression() != jetElement) {
parent
} else {
null
}
}
is JetOperationExpression -> {
if (parent.getOperationReference() == jetElement) {
parent
} else {
null
}
}
else -> null
}
if (newExpression is JetExpression) return newExpression
if (jetElement is JetSimpleNameExpression) {
return jetElement
}
return null
}
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.jet.JetNodeType
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.psi.JetExpressionCodeFragmentImpl
import com.intellij.psi.PsiCodeBlock
class KotlinCodeFragmentFactory: CodeFragmentFactory() {
override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
@@ -45,6 +46,9 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
}
override fun isContextAccepted(contextElement: PsiElement?): Boolean {
if (contextElement is PsiCodeBlock) {
return contextElement.getContext()?.getContext()?.getLanguage() == JetFileType.INSTANCE.getLanguage()
}
return contextElement?.getLanguage() == JetFileType.INSTANCE.getLanguage()
}
@@ -0,0 +1,5 @@
fun foo() {
1 <caret>+ 1
}
// EXPECTED: 1 + 1
@@ -0,0 +1,7 @@
fun foo() {
<caret>bar()
}
fun bar() = 1
// EXPECTED: bar()
@@ -0,0 +1,4 @@
val a = 1
val b = <caret>a
// EXPECTED: a
@@ -0,0 +1,7 @@
fun foo() {
1.<caret>foo()
}
fun Int.foo() = 1
// EXPECTED: 1.foo()
@@ -0,0 +1,8 @@
fun foo() {
val a = 1
bar(<caret>a)
}
fun bar(i: Int) = 1
// EXPECTED: a
@@ -0,0 +1,7 @@
fun foo() {
<caret>bar { }
}
fun bar(f: () -> Unit) = 1
// EXPECTED: bar { }
@@ -0,0 +1,10 @@
fun foo() {
val klass = MyClass()
klass<caret>[1]
}
class MyClass {
fun get(i: Int): Int = 1
}
// EXPECTED: klass[1]
@@ -0,0 +1,7 @@
fun foo() {
1 <caret>foo 1
}
fun Int.foo(i: Int) = 1
// EXPECTED: 1 foo 1
@@ -0,0 +1,8 @@
fun foo() {
val a = 1
<caret>a foo 1
}
fun Int.foo(i: Int) = 1
// EXPECTED: a
@@ -0,0 +1,5 @@
fun foo() {
1 <caret>is Int
}
// EXPECTED: 1 is Int
@@ -0,0 +1,3 @@
<caret>val a = 1
// EXPECTED: null
@@ -0,0 +1,3 @@
<caret>public fun foo() = 1
// EXPECTED: null
@@ -0,0 +1,5 @@
fun foo(i: Int) {
<caret>i
}
// EXPECTED: i
@@ -0,0 +1,6 @@
val a = 1
fun foo() {
<caret>a
}
// EXPECTED: a
@@ -0,0 +1,3 @@
val <caret>a = 1
// EXPECTED: null
@@ -0,0 +1,10 @@
fun foo() {
val klass = MyClass()
klass.<caret>bar
}
class MyClass {
val bar = 1
}
// EXPECTED: klass.bar
@@ -0,0 +1,10 @@
fun foo() {
val klass = MyClass()
<caret>klass.bar()
}
class MyClass {
fun bar() = 1
}
// EXPECTED: klass
@@ -0,0 +1,10 @@
fun foo() {
val klass = MyClass()
klass.<caret>bar()
}
class MyClass {
fun bar() = 1
}
// EXPECTED: klass.bar()
@@ -0,0 +1,11 @@
class Derived: Base() {
fun test() {
<caret>super.test()
}
}
open class Base {
fun test() {}
}
// EXPECTED: super.test()
@@ -0,0 +1,7 @@
class MyClass {
fun test() {
<caret>this.test()
}
}
// EXPECTED: this.test()
@@ -0,0 +1,7 @@
class MyClass {
fun Int.test() {
<caret>this@MyClass
}
}
// EXPECTED: this@MyClass
@@ -0,0 +1,5 @@
fun foo() {
<caret>+1
}
// EXPECTED: +1
@@ -77,7 +77,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
private fun SuspendContextImpl.evaluate(expression: String, expectedResult: String) {
try {
val sourcePosition = ContextUtil.getSourcePosition(this)
val contextElement = sourcePosition?.getElementAt()!!
val contextElement = ContextUtil.getContextElement(sourcePosition)!!
Assert.assertTrue("KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. ContextElement = ${contextElement.getText()}",
KotlinCodeFragmentFactory().isContextAccepted(contextElement))
@@ -0,0 +1,38 @@
/*
* 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.evaluate
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider
import org.jetbrains.jet.plugin.PluginTestCaseBase
import org.jetbrains.jet.InTextDirectivesUtils
import org.junit.Assert
public abstract class AbstractSelectExpressionForDebuggerTest : LightCodeInsightFixtureTestCase() {
fun doTest(path: String) {
myFixture.configureByFile(path)
val elementAt = myFixture.getFile()?.findElementAt(myFixture.getCaretOffset())!!
val selectedExpression = KotlinEditorTextProvider.findExpressionInner(elementAt)
val expected = InTextDirectivesUtils.findStringWithPrefixes(myFixture.getFile()?.getText()!!, "// EXPECTED: ")
Assert.assertEquals("Another expression should be selected", expected, selectedExpression?.getText() ?: "null")
}
override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/debugger/selectExpression";
}
@@ -0,0 +1,149 @@
/*
* 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.evaluate;
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.evaluate.AbstractSelectExpressionForDebuggerTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/debugger/selectExpression")
public class SelectExpressionForDebuggerTestGenerated extends AbstractSelectExpressionForDebuggerTest {
public void testAllFilesPresentInSelectExpression() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/debugger/selectExpression"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("binaryExpression.kt")
public void testBinaryExpression() throws Exception {
doTest("idea/testData/debugger/selectExpression/binaryExpression.kt");
}
@TestMetadata("call.kt")
public void testCall() throws Exception {
doTest("idea/testData/debugger/selectExpression/call.kt");
}
@TestMetadata("expressionInPropertyInitializer.kt")
public void testExpressionInPropertyInitializer() throws Exception {
doTest("idea/testData/debugger/selectExpression/expressionInPropertyInitializer.kt");
}
@TestMetadata("extensionFun.kt")
public void testExtensionFun() throws Exception {
doTest("idea/testData/debugger/selectExpression/extensionFun.kt");
}
@TestMetadata("funArgument.kt")
public void testFunArgument() throws Exception {
doTest("idea/testData/debugger/selectExpression/funArgument.kt");
}
@TestMetadata("functionLiteral.kt")
public void testFunctionLiteral() throws Exception {
doTest("idea/testData/debugger/selectExpression/functionLiteral.kt");
}
@TestMetadata("getConvention.kt")
public void testGetConvention() throws Exception {
doTest("idea/testData/debugger/selectExpression/getConvention.kt");
}
@TestMetadata("infixCall.kt")
public void testInfixCall() throws Exception {
doTest("idea/testData/debugger/selectExpression/infixCall.kt");
}
@TestMetadata("infixCallArgument.kt")
public void testInfixCallArgument() throws Exception {
doTest("idea/testData/debugger/selectExpression/infixCallArgument.kt");
}
@TestMetadata("isExpression.kt")
public void testIsExpression() throws Exception {
doTest("idea/testData/debugger/selectExpression/isExpression.kt");
}
@TestMetadata("keyword.kt")
public void testKeyword() throws Exception {
doTest("idea/testData/debugger/selectExpression/keyword.kt");
}
@TestMetadata("modifier.kt")
public void testModifier() throws Exception {
doTest("idea/testData/debugger/selectExpression/modifier.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
doTest("idea/testData/debugger/selectExpression/param.kt");
}
@TestMetadata("propertyCall.kt")
public void testPropertyCall() throws Exception {
doTest("idea/testData/debugger/selectExpression/propertyCall.kt");
}
@TestMetadata("propertyDeclaration.kt")
public void testPropertyDeclaration() throws Exception {
doTest("idea/testData/debugger/selectExpression/propertyDeclaration.kt");
}
@TestMetadata("qualifiedExpressionProperty.kt")
public void testQualifiedExpressionProperty() throws Exception {
doTest("idea/testData/debugger/selectExpression/qualifiedExpressionProperty.kt");
}
@TestMetadata("qualifiedExpressionReceiver.kt")
public void testQualifiedExpressionReceiver() throws Exception {
doTest("idea/testData/debugger/selectExpression/qualifiedExpressionReceiver.kt");
}
@TestMetadata("qualifiedExpressionSelector.kt")
public void testQualifiedExpressionSelector() throws Exception {
doTest("idea/testData/debugger/selectExpression/qualifiedExpressionSelector.kt");
}
@TestMetadata("super.kt")
public void testSuper() throws Exception {
doTest("idea/testData/debugger/selectExpression/super.kt");
}
@TestMetadata("this.kt")
public void testThis() throws Exception {
doTest("idea/testData/debugger/selectExpression/this.kt");
}
@TestMetadata("thisWithLabel.kt")
public void testThisWithLabel() throws Exception {
doTest("idea/testData/debugger/selectExpression/thisWithLabel.kt");
}
@TestMetadata("unaryExpression.kt")
public void testUnaryExpression() throws Exception {
doTest("idea/testData/debugger/selectExpression/unaryExpression.kt");
}
}