diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinEditorTextProvider.kt b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinEditorTextProvider.kt index 62a00c6d5e5..4a550c1ce2c 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinEditorTextProvider.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinEditorTextProvider.kt @@ -23,14 +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 org.jetbrains.jet.lang.psi.JetExpressionCodeFragmentImpl class KotlinEditorTextProvider : EditorTextProvider { override fun getEditorText(elementAtCaret: PsiElement): TextWithImports? { - return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, elementAtCaret.getText()) + return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, elementAtCaret.getText(), getImports(elementAtCaret), JetFileType.INSTANCE) } override fun findExpression(elementAtCaret: PsiElement, allowMethodCalls: Boolean): Pair? { return Pair(elementAtCaret, elementAtCaret.getTextRange()) } + + class object { + fun getImports(elementAtCaret: PsiElement): String { + val containingFile = elementAtCaret.getContainingFile() + if (containingFile !is JetFile) return "" + + return containingFile.getImportList()?.getImports() + ?.map { it.getText() } + ?.makeString(JetExpressionCodeFragmentImpl.IMPORT_SEPARATOR) ?: "" + } + } } 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 03c4f8ea3fe..312f7559d49 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -33,7 +33,11 @@ import org.jetbrains.jet.lang.psi.JetExpressionCodeFragmentImpl class KotlinCodeFragmentFactory: CodeFragmentFactory() { override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { - return JetExpressionCodeFragmentImpl(project, "fragment.kt", item.getText(), context) + val codeFragment = JetExpressionCodeFragmentImpl(project, "fragment.kt", item.getText(), context) + if (item.getImports().isNotEmpty()) { + 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 d0e6bfb36fc..86f2793ff63 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -50,10 +50,27 @@ import org.jetbrains.jet.lang.resolve.java.PackageClassUtils import org.jetbrains.jet.lang.resolve.name.FqName import org.jetbrains.jet.plugin.caches.resolve.KotlinDeclarationsCache import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import java.io.File +import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider import org.jetbrains.jet.OutputFileCollection +import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment +import org.jetbrains.jet.lang.psi.JetExpressionCodeFragmentImpl object KotlinEvaluationBuilder: EvaluatorBuilder { override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator { + if (codeFragment !is JetExpressionCodeFragment) { + return EvaluatorBuilderImpl.getInstance()!!.build(codeFragment, position) + } + + val elementAt = position?.getElementAt() + if (elementAt != null) { + codeFragment.addImportsFromString(KotlinEditorTextProvider.getImports(elementAt)) + + val packageName = (elementAt.getContainingFile() as JetFile).getPackageDirective()?.getFqName()?.asString() + if (packageName != null) { + codeFragment.addImportsFromString("import $packageName.*") + } + } return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment)) } } @@ -62,10 +79,11 @@ class KotlinEvaluator(val codeFragment: PsiElement) : Evaluator { override fun evaluate(context: EvaluationContextImpl): Any? { return ApplicationManager.getApplication()?.runReadAction(object: Computable { override fun compute(): Any? { - val fileText = template.replace("!EXPRESSION!", codeFragment.getText()) - val virtualFile = LightVirtualFile("debugFile.kt", JetLanguage.INSTANCE, fileText) - virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET) - val file = (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile + if (codeFragment !is JetExpressionCodeFragment) { + throw AssertionError("KotlinEvaluator should be invoke only for KotlinCodeFragment") + } + + val file = createFileForDebugger(codeFragment) val analyzeExhaust = AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegrationAndCheckForErrors( file.getProject(), @@ -80,7 +98,7 @@ class KotlinEvaluator(val codeFragment: PsiElement) : Evaluator { AnalyzingUtils.throwExceptionOnErrors(bindingContext) } catch (e: IllegalStateException) { - throw EvaluateExceptionUtil.createEvaluateException(e.getMessage()) + exception(e.getMessage() ?: "Exception from kotlin compiler") } val state = GenerationState( @@ -146,9 +164,24 @@ class KotlinEvaluator(val codeFragment: PsiElement) : Evaluator { private val template = """ package packageForDebugger +!IMPORT_LIST! + fun debugFun() = run { !EXPRESSION! } """ -private val packageInternalName = PackageClassUtils.getPackageClassFqName(FqName("packageForDebugger")).asString().replace(".", "/") \ No newline at end of file +private val packageInternalName = PackageClassUtils.getPackageClassFqName(FqName("packageForDebugger")).asString().replace(".", "/") + +private fun createFileForDebugger(codeFragment: JetExpressionCodeFragment): JetFile { + var fileText = template.replace("!EXPRESSION!", codeFragment.getText()) + fileText = fileText.replace("!IMPORT_LIST!", + codeFragment.importsToString() + .split(JetExpressionCodeFragmentImpl.IMPORT_SEPARATOR) + .makeString("\n")) + + val virtualFile = LightVirtualFile("debugFile.kt", JetLanguage.INSTANCE, fileText) + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET) + return (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl) + .trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile +} diff --git a/idea/testData/debugger/tinyApp/outs/imports.out b/idea/testData/debugger/tinyApp/outs/imports.out new file mode 100644 index 00000000000..41c860627fb --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/imports.out @@ -0,0 +1,7 @@ +LineBreakpoint created at imports.kt:10 +!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! imports.ImportsPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +imports.kt:9 +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/imports.kt b/idea/testData/debugger/tinyApp/src/evaluate/imports.kt new file mode 100644 index 00000000000..10c9a801371 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/imports.kt @@ -0,0 +1,23 @@ +package imports + +import java.util.Collections +import java.util.ArrayList; +import java.util.HashSet +import java.util.HashMap as JHashMap + +fun main(args: Array) { + //Breakpoint! + args.size +} + +// EXPRESSION: Collections.emptyList() +// RESULT: instance of java.util.Collections$EmptyList(id=337): Ljava/util/Collections$EmptyList; + +// EXPRESSION: ArrayList() +// RESULT: instance of java.util.ArrayList(id=383): Ljava/util/ArrayList; + +// EXPRESSION: HashSet() +// RESULT: instance of java.util.HashSet(id=387): Ljava/util/HashSet; + +// EXPRESSION: JHashMap() +// RESULT: instance of java.util.HashMap(id=391): Ljava/util/HashMap; 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 d9c952f8332..80e1519134d 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -77,12 +77,12 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC private fun SuspendContextImpl.evaluate(expression: String, expectedResult: String) { try { val sourcePosition = ContextUtil.getSourcePosition(this) - val contextElement = sourcePosition?.getElementAt() - Assert.assertTrue("KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. ContextElement = ${contextElement?.getText()}", + val contextElement = sourcePosition?.getElementAt()!! + Assert.assertTrue("KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. ContextElement = ${contextElement.getText()}", KotlinCodeFragmentFactory().isContextAccepted(contextElement)) val evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject()) { - EvaluatorBuilderImpl.build(TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression, "", JetFileType.INSTANCE), + EvaluatorBuilderImpl.build(TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression, KotlinEditorTextProvider.getImports(contextElement), JetFileType.INSTANCE), contextElement, sourcePosition) } diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 64d717ce41e..099b5fd1000 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -51,6 +51,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doTest("idea/testData/debugger/tinyApp/src/evaluate/collections.kt"); } + @TestMetadata("imports.kt") + public void testImports() throws Exception { + doTest("idea/testData/debugger/tinyApp/src/evaluate/imports.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { doTest("idea/testData/debugger/tinyApp/src/evaluate/simple.kt");