diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt index 54824026f66..904fd0125a0 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -29,17 +29,19 @@ import com.intellij.psi.JavaCodeFragmentFactory import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.lang.psi.JetBlockExpression +import org.jetbrains.jet.lang.psi.JetBlockCodeFragment class KotlinCodeFragmentFactory: CodeFragmentFactory() { override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { - if (item.getKind() == CodeFragmentKind.EXPRESSION) { - val codeFragment = JetExpressionCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context)) - if (item.getImports().isNotEmpty()) { - codeFragment.addImportsFromString(item.getImports()) - } - return codeFragment + val codeFragment = if (item.getKind() == CodeFragmentKind.EXPRESSION) { + JetExpressionCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context)) } - return JavaCodeFragmentFactory.getInstance(project)!!.createCodeBlockCodeFragment(item.getText(), context, true) + else { + JetBlockCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context)) + } + codeFragment.addImportsFromString(item.getImports()) + return codeFragment } override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt index da7bf955513..5f00f3372d9 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -48,8 +48,6 @@ import org.jetbrains.eval4j.jdi.asValue import org.jetbrains.jet.plugin.refactoring.createTempCopy import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis -import org.jetbrains.jet.plugin.util.MaybeError -import org.jetbrains.jet.plugin.util.MaybeValue import org.jetbrains.jet.plugin.refactoring.extractFunction.validate import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction import org.jetbrains.jet.lang.psi.JetNamedFunction @@ -61,13 +59,14 @@ import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults import org.jetbrains.jet.lang.psi.JetCodeFragment import org.jetbrains.jet.lang.psi.JetImportList -import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.lang.psi.codeFragmentUtil.setSkipVisibilityCheck +import org.jetbrains.jet.lang.psi.JetBlockCodeFragment +import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status object KotlinEvaluationBuilder: EvaluatorBuilder { override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator { - if (codeFragment !is JetExpressionCodeFragment || position == null) { + if (codeFragment !is JetCodeFragment || position == null) { return EvaluatorBuilderImpl.getInstance()!!.build(codeFragment, position) } @@ -78,11 +77,11 @@ object KotlinEvaluationBuilder: EvaluatorBuilder { codeFragment.addImportsFromString("import $packageName.*") } } - return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetExpressionCodeFragment, position)) + return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetCodeFragment, position)) } } -class KotlinEvaluator(val codeFragment: JetExpressionCodeFragment, +class KotlinEvaluator(val codeFragment: JetCodeFragment, val sourcePosition: SourcePosition ) : Evaluator { override fun evaluate(context: EvaluationContextImpl): Any? { @@ -214,7 +213,7 @@ package packageForDebugger private val packageInternalName = PackageClassUtils.getPackageClassFqName(FqName("packageForDebugger")).asString().replace(".", "/") -private fun createFileForDebugger(codeFragment: JetExpressionCodeFragment, +private fun createFileForDebugger(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction ): JetFile { var fileText = template.replace("!IMPORT_LIST!", @@ -247,11 +246,19 @@ fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) { } } -fun addDebugExpressionBeforeContextElement(debugExpression: JetExpression, contextElement: PsiElement): JetExpression? { +fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? { val parent = contextElement.getParent() if (parent == null) return null parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement) + + val debugExpression = when(codeFragment) { + is JetExpressionCodeFragment -> codeFragment.getExpression() + is JetBlockCodeFragment -> codeFragment.getBlock() + else -> null + } + if (debugExpression == null) return null + val newDebugExpression = parent.addBefore(debugExpression, contextElement) if (newDebugExpression == null) return null @@ -261,14 +268,12 @@ fun addDebugExpressionBeforeContextElement(debugExpression: JetExpression, conte } private fun getFunctionForExtractedFragment( - codeFragment: JetExpressionCodeFragment, + codeFragment: JetCodeFragment, breakpointFile: PsiFile, breakpointLine: Int ): JetNamedFunction? { return ApplicationManager.getApplication()?.runReadAction(object: Computable { override fun compute(): JetNamedFunction? { - val project = codeFragment.getProject() - val originalFile = breakpointFile as JetFile val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine) @@ -284,8 +289,7 @@ private fun getFunctionForExtractedFragment( addImportsToFile(codeFragment.importsAsImportList(), tmpFile) - val debugExpression = JetPsiFactory.createExpression(project, codeFragment.getText()) - val newDebugExpression = addDebugExpressionBeforeContextElement(debugExpression, contextElement) + val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement) if (newDebugExpression == null) return null val nextSibling = tmpFile.getDeclarations().firstOrNull() @@ -293,7 +297,7 @@ private fun getFunctionForExtractedFragment( val analysisResult = ExtractionData(tmpFile, Collections.singletonList(newDebugExpression), nextSibling).performAnalysis() if (analysisResult.status != Status.SUCCESS) { - throw EvaluateExceptionUtil.createEvaluateException(analysisResult.messages.makeString("\n")) + throw EvaluateExceptionUtil.createEvaluateException(analysisResult.messages.makeString(", ")) } val validationResult = analysisResult.descriptor!!.validate() diff --git a/idea/testData/debugger/tinyApp/outs/collections.out b/idea/testData/debugger/tinyApp/outs/collections.out index f2285270e1b..618a4970607 100644 --- a/idea/testData/debugger/tinyApp/outs/collections.out +++ b/idea/testData/debugger/tinyApp/outs/collections.out @@ -1,7 +1,7 @@ -LineBreakpoint created at collections.kt:5 +LineBreakpoint created at collections.kt:6 !JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! collections.CollectionsPackage Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' -collections.kt:4 +collections.kt:5 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/collections.kt b/idea/testData/debugger/tinyApp/src/evaluate/collections.kt index 6737ac6d09c..7499e3ab3ab 100644 --- a/idea/testData/debugger/tinyApp/src/evaluate/collections.kt +++ b/idea/testData/debugger/tinyApp/src/evaluate/collections.kt @@ -1,6 +1,7 @@ package collections fun main(args: Array) { + val ar = intArray(1, 2, 100, 200) //Breakpoint! args.size } diff --git a/idea/testData/debugger/tinyApp/src/evaluate/collections.kt.fragment b/idea/testData/debugger/tinyApp/src/evaluate/collections.kt.fragment new file mode 100644 index 00000000000..8c2be2df173 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/collections.kt.fragment @@ -0,0 +1,5 @@ +ar.map { if (it > 50) "big" else "small" } + .filter { it == "small" } + .size + +// RESULT: 2: I \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/simple.kt.fragment b/idea/testData/debugger/tinyApp/src/evaluate/simple.kt.fragment new file mode 100644 index 00000000000..4c5b81a3e61 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/simple.kt.fragment @@ -0,0 +1,4 @@ +val a = 1 +a + args.size + +// RESULT: 1: I diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index c523aced6fd..6ab59e7fd74 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -33,14 +33,18 @@ import org.jetbrains.eval4j.Value import org.jetbrains.eval4j.ObjectValue import com.sun.jdi.ObjectReference import org.jetbrains.jet.lang.psi.JetCodeFragment +import java.util.Collections public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() { fun doTest(path: String) { - val fileContent = FileUtil.loadFile(File(path)) + val file = File(path) + val fileContent = FileUtil.loadFile(file, true) val expressions = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// EXPRESSION: ") - val expectedResults = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// RESULT: ") + val expectedExpressionResults = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContent, "// RESULT: ") + assert(expressions.size == expectedExpressionResults.size, "Sizes of test directives are different") - assert(expressions.size == expectedResults.size, "Sizes of test directives are different") + val blocks = findFilesWithBlocks(file).map { FileUtil.loadFile(it, true) } + val expectedBlockResults = blocks.map { InTextDirectivesUtils.findStringWithPrefixes(it, "// RESULT: ") ?: throw AssertionError("Couldn't find expected result for block: $it") } createDebugProcess(path) @@ -48,13 +52,22 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC val exceptions = linkedMapOf() for ((i, expression) in expressions.withIndices()) { try { - evaluate(expression, expectedResults[i]) + evaluate(expression, CodeFragmentKind.EXPRESSION, expectedExpressionResults[i]) } catch (e: Throwable) { exceptions.put(expression, e) } } + for ((i, block) in blocks.withIndices()) { + try { + evaluate(block, CodeFragmentKind.CODE_BLOCK, expectedBlockResults[i]) + } + catch (e: Throwable) { + exceptions.put(block, e) + } + } + if (!exceptions.empty) { for (exc in exceptions.values()) { exc.printStackTrace() @@ -65,6 +78,11 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC finish() } + private fun findFilesWithBlocks(mainFile: File): List { + val mainFileName = mainFile.getName() + return mainFile.getParentFile()?.listFiles()?.filter { it.name.startsWith(mainFileName) && it.name != mainFileName } ?: Collections.emptyList() + } + private fun onBreakpoint(doOnBreakpoint: SuspendContextImpl.() -> Unit) { super.onBreakpoint { super.printContext(it) @@ -78,7 +96,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC } } - private fun SuspendContextImpl.evaluate(expression: String, expectedResult: String) { + private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String) { try { val sourcePosition = ContextUtil.getSourcePosition(this) val contextElement = ContextUtil.getContextElement(sourcePosition)!! @@ -87,8 +105,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC val evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject()) { EvaluatorBuilderImpl.build(TextWithImportsImpl( - CodeFragmentKind.EXPRESSION, - expression, + codeFragmentKind, + text, JetCodeFragment.getImportsForElement(contextElement), JetFileType.INSTANCE), contextElement, @@ -98,7 +116,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC val value = evaluator.evaluate(createEvaluationContext(this)) val actualResult = value.asValue().asString() - Assert.assertTrue("Evaluate expression returns wrong result for $expression:\nexpected = $expectedResult\nactual = $actualResult\n", expectedResult == actualResult) + Assert.assertTrue("Evaluate expression returns wrong result for $text:\nexpected = $expectedResult\nactual = $actualResult\n", expectedResult == actualResult) } finally { resume(this)