Evaluate expression: add ability to import class

This commit is contained in:
Natalia Ukhorskaya
2014-04-28 14:06:57 +04:00
parent 3f95cdb7bb
commit 98532f321d
18 changed files with 200 additions and 60 deletions
@@ -40,6 +40,9 @@ public abstract class JetCodeFragment(
{
getViewProvider().forceCachedPsi(this)
init(TokenType.CODE_FRAGMENT, elementType)
if (_context != null) {
addImportsFromString(getImportsForElement(_context))
}
}
private var _resolveScope: GlobalSearchScope? = null
@@ -89,11 +92,15 @@ public abstract class JetCodeFragment(
}
override fun addImportsFromString(imports: String?) {
if (imports == null) return
if (imports == null || imports.isEmpty()) return
_myImports.addAll(imports.split(IMPORT_SEPARATOR))
}
fun importsAsImportList(): JetImportList? {
return JetPsiFactory.createFile(_project, _myImports.makeString("\n")).getImportList()
}
override fun setVisibilityChecker(checker: JavaCodeFragment.VisibilityChecker?) { }
override fun getVisibilityChecker() = JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE
@@ -110,5 +117,14 @@ public abstract class JetCodeFragment(
class object {
val IMPORT_SEPARATOR = ","
fun getImportsForElement(elementAtCaret: PsiElement): String {
val containingFile = elementAtCaret.getContainingFile()
if (containingFile !is JetFile) return ""
return containingFile.getImportList()?.getImports()
?.map { it.getText() }
?.makeString(JetCodeFragment.IMPORT_SEPARATOR) ?: ""
}
}
}
@@ -24,8 +24,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetCodeFragment;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetImportDirective;
import org.jetbrains.jet.lang.psi.JetImportList;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.name.LabelName;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -158,10 +160,19 @@ public class LazyImportScope implements JetScope, LazyEntity {
@NotNull BindingTrace traceForImportResolve,
@NotNull String debugName
) {
List<JetImportDirective> importDirectives;
if (jetFile instanceof JetCodeFragment) {
JetImportList importList = ((JetCodeFragment) jetFile).importsAsImportList();
importDirectives = importList != null ? importList.getImports() : Collections.<JetImportDirective>emptyList();
}
else {
importDirectives = jetFile.getImportDirectives();
}
return new LazyImportScope(
resolveSession,
packageDescriptor,
Lists.reverse(jetFile.getImportDirectives()),
Lists.reverse(importDirectives),
traceForImportResolve,
debugName,
packageDescriptor.getFqName().isRoot());
@@ -269,7 +269,8 @@ fun main(args: Array<String>) {
}
testClass(javaClass<AbstractCodeFragmentHighlightingTest>()) {
model("checker/codeFragments", extension = "kt")
model("checker/codeFragments", extension = "kt", recursive = false)
model("checker/codeFragments/imports", testMethod = "doTestWithImport", extension = "kt")
}
testClass(javaClass<AbstractJetJsCheckerTest>()) {
@@ -40,7 +40,7 @@ import org.jetbrains.jet.lang.psi.JetCodeFragment
class KotlinEditorTextProvider : EditorTextProvider {
override fun getEditorText(elementAtCaret: PsiElement): TextWithImports? {
val expression = findExpressionInner(elementAtCaret)
return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression?.getText() ?: "", getImports(elementAtCaret), JetFileType.INSTANCE)
return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression?.getText() ?: "", JetCodeFragment.getImportsForElement(elementAtCaret), JetFileType.INSTANCE)
}
override fun findExpression(elementAtCaret: PsiElement, allowMethodCalls: Boolean): Pair<PsiElement, TextRange>? {
@@ -50,15 +50,6 @@ class KotlinEditorTextProvider : EditorTextProvider {
}
class object {
fun getImports(elementAtCaret: PsiElement): String {
val containingFile = elementAtCaret.getContainingFile()
if (containingFile !is JetFile) return ""
return containingFile.getImportList()?.getImports()
?.map { it.getText() }
?.makeString(JetCodeFragment.IMPORT_SEPARATOR) ?: ""
}
fun findExpressionInner(element: PsiElement): JetExpression? {
val jetElement = PsiTreeUtil.getParentOfType(element, javaClass<JetElement>())
if (jetElement == null) return null
@@ -66,6 +66,9 @@ import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment
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.resolve.ImportPath
import org.jetbrains.jet.lang.psi.JetImportList
import org.jetbrains.jet.lang.psi.JetExpression
object KotlinEvaluationBuilder: EvaluatorBuilder {
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
@@ -75,18 +78,16 @@ object KotlinEvaluationBuilder: EvaluatorBuilder {
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, position))
return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetExpressionCodeFragment, position))
}
}
class KotlinEvaluator(val codeFragment: PsiElement,
class KotlinEvaluator(val codeFragment: JetExpressionCodeFragment,
val sourcePosition: SourcePosition
) : Evaluator {
override fun evaluate(context: EvaluationContextImpl): Any? {
@@ -234,8 +235,36 @@ private fun createFileForDebugger(codeFragment: JetExpressionCodeFragment,
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
}
fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
if (newImportList != null) {
val tmpFileImportList = tmpFile.getImportList()
val packageDirective = tmpFile.getPackageDirective()
if (tmpFileImportList == null) {
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
}
else {
tmpFileImportList.replace(newImportList)
}
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
}
}
fun addDebugExpressionBeforeContextElement(debugExpression: JetExpression, contextElement: PsiElement): JetExpression? {
val parent = contextElement.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
return newDebugExpression as JetExpression
}
private fun getFunctionForExtractedFragment(
codeFragment: PsiElement,
codeFragment: JetExpressionCodeFragment,
breakpointFile: PsiFile,
breakpointLine: Int
): JetNamedFunction? {
@@ -252,19 +281,14 @@ private fun getFunctionForExtractedFragment(
val elementAtOffset = tmpFile.findElementAt(lineStart)
if (elementAtOffset == null) return null
val element: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val debugExpression = JetPsiFactory.createExpression(project, codeFragment.getText())
val parent = element.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(project), element)
val newDebugExpression = parent.addBefore(debugExpression, element)
val newDebugExpression = addDebugExpressionBeforeContextElement(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(project), element)
val nextSibling = tmpFile.getDeclarations().firstOrNull()
if (nextSibling == null) return null
@@ -38,6 +38,7 @@ import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.jet.lang.types.TypeUtils;
@@ -228,10 +229,18 @@ public class ResolveElementCache {
JetScope scopeForContextElement = contextForElement.get(BindingContext.RESOLUTION_SCOPE, contextExpression);
if (scopeForContextElement != null) {
JetScope codeFragmentScope = resolveSession.getScopeProvider().getFileScope(codeFragment);
ChainedScope chainedScope = new ChainedScope(
scopeForContextElement.getContainingDeclaration(),
"Scope for resolve code fragment",
scopeForContextElement,
codeFragmentScope
);
DataFlowInfo dataFlowInfoForContextElement = contextForElement.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, contextExpression);
AnalyzerPackage.computeTypeInContext(
codeFragment.getExpression(),
scopeForContextElement,
chainedScope,
trace,
dataFlowInfoForContextElement == null ? DataFlowInfo.EMPTY : dataFlowInfoForContextElement,
TypeUtils.NO_EXPECTED_TYPE,
@@ -309,6 +309,11 @@ public class AutoImportFix extends JetHintAction<JetSimpleNameExpression> implem
return null;
}
@Override
public boolean isApplicableForCodeFragment() {
return true;
}
};
}
}
@@ -97,9 +97,11 @@ public class ImportInsertHelper {
public static void writeImportToFile(@NotNull ImportPath importPath, @NotNull JetFile file) {
if (file instanceof JetCodeFragment) {
// TODO Insert import doesn't work for codeFragments yet
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importPath);
((JetCodeFragment) file).addImportsFromString(newDirective.getText());
return;
}
JetImportList importList = file.getImportList();
if (importList != null) {
JetImportDirective newDirective = JetPsiFactory.createImportDirective(file.getProject(), importPath);
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetCodeFragment;
import org.jetbrains.jet.lang.psi.JetFile;
public abstract class JetIntentionAction<T extends PsiElement> implements IntentionAction {
@@ -35,7 +36,7 @@ public abstract class JetIntentionAction<T extends PsiElement> implements Intent
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid() && file.getManager().isInProject(file) && (file instanceof JetFile);
return element.isValid() && (file.getManager().isInProject(file) || file instanceof JetCodeFragment) && (file instanceof JetFile);
}
//Don't override this method. Use the method with JetFile instead.
@@ -35,7 +35,7 @@ public abstract class JetSingleIntentionActionFactory implements JetIntentionAct
public final List<IntentionAction> createActions(Diagnostic diagnostic) {
List<IntentionAction> intentionActionList = new LinkedList<IntentionAction>();
if (diagnostic.getPsiElement().getContainingFile() instanceof JetCodeFragment) {
if (diagnostic.getPsiElement().getContainingFile() instanceof JetCodeFragment && !isApplicableForCodeFragment()) {
return intentionActionList;
}
@@ -45,4 +45,8 @@ public abstract class JetSingleIntentionActionFactory implements JetIntentionAct
}
return intentionActionList;
}
public boolean isApplicableForCodeFragment() {
return false;
}
}
@@ -0,0 +1,5 @@
fun foo() {
<caret>val a = 1
}
// IMPORT: java.util.HashMap
@@ -0,0 +1 @@
HashMap<Int, Int>()
@@ -0,0 +1,7 @@
LineBreakpoint created at classFromAnotherPackage.kt:7
!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! classFromAnotherPackage.ClassFromAnotherPackagePackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
classFromAnotherPackage.kt:6
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,14 @@
package classFromAnotherPackage
import stepInto.MyJavaClass
fun main(args: Array<String>) {
//Breakpoint!
args.size
}
// EXPRESSION: MyJavaClass()
// RESULT: instance of stepInto.MyJavaClass(id=ID): LstepInto/MyJavaClass;
// EXPRESSION: stepInto.MyJavaClass()
// RESULT: instance of stepInto.MyJavaClass(id=ID): LstepInto/MyJavaClass;
@@ -24,14 +24,30 @@ import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment
import com.intellij.openapi.util.io.FileUtil
import java.io.File
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.InTextDirectivesUtils
abstract class AbstractCodeFragmentHighlightingTest: AbstractJetPsiCheckerTest() {
abstract class AbstractCodeFragmentHighlightingTest : AbstractJetPsiCheckerTest() {
override fun doTest(filePath: String) {
myFixture.configureByCodeFragment(filePath)
myFixture.checkHighlighting(true, false, false)
}
fun doTestWithImport(filePath: String) {
myFixture.configureByCodeFragment(filePath)
ApplicationManager.getApplication()?.runWriteAction {
val fileText = FileUtil.loadFile(File(filePath), true)
InTextDirectivesUtils.findListWithPrefixes(fileText, "// IMPORT: ").forEach {
ImportInsertHelper.addImportDirectiveIfNeeded(FqName(it), (myFixture.getFile() as JetFile))
}
}
myFixture.checkHighlighting(true, false, false)
}
}
abstract class AbstractCodeFragmentCompletionTest : AbstractJvmBasicCompletionTest() {
@@ -32,6 +32,7 @@ import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.eval4j.Value
import org.jetbrains.eval4j.ObjectValue
import com.sun.jdi.ObjectReference
import org.jetbrains.jet.lang.psi.JetCodeFragment
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestCase() {
fun doTest(path: String) {
@@ -85,7 +86,11 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestC
KotlinCodeFragmentFactory().isContextAccepted(contextElement))
val evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject()) {
EvaluatorBuilderImpl.build(TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression, KotlinEditorTextProvider.getImports(contextElement), JetFileType.INSTANCE),
EvaluatorBuilderImpl.build(TextWithImportsImpl(
CodeFragmentKind.EXPRESSION,
expression,
JetCodeFragment.getImportsForElement(contextElement),
JetFileType.INSTANCE),
contextElement,
sourcePosition)
}
@@ -30,35 +30,58 @@ import org.jetbrains.jet.plugin.debugger.evaluate.AbstractCodeFragmentHighlighti
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/checker/codeFragments")
@InnerTestClasses({CodeFragmentHighlightingTestGenerated.CodeFragments.class, CodeFragmentHighlightingTestGenerated.Imports.class})
public class CodeFragmentHighlightingTestGenerated extends AbstractCodeFragmentHighlightingTest {
public void testAllFilesPresentInCodeFragments() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/checker/codeFragments"), Pattern.compile("^(.+)\\.kt$"), true);
@TestMetadata("idea/testData/checker/codeFragments")
public static class CodeFragments extends AbstractCodeFragmentHighlightingTest {
public void testAllFilesPresentInCodeFragments() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/checker/codeFragments"), Pattern.compile("^(.+)\\.kt$"), false);
}
@TestMetadata("binaryExpression.kt")
public void testBinaryExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/binaryExpression.kt");
}
@TestMetadata("callExpression.kt")
public void testCallExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/callExpression.kt");
}
@TestMetadata("contextElementAsStatement.kt")
public void testContextElementAsStatement() throws Exception {
doTest("idea/testData/checker/codeFragments/contextElementAsStatement.kt");
}
@TestMetadata("simpleNameExpression.kt")
public void testSimpleNameExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/simpleNameExpression.kt");
}
@TestMetadata("smartCasts.kt")
public void testSmartCasts() throws Exception {
doTest("idea/testData/checker/codeFragments/smartCasts.kt");
}
}
@TestMetadata("binaryExpression.kt")
public void testBinaryExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/binaryExpression.kt");
@TestMetadata("idea/testData/checker/codeFragments/imports")
public static class Imports extends AbstractCodeFragmentHighlightingTest {
public void testAllFilesPresentInImports() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/checker/codeFragments/imports"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("hashMap.kt")
public void testHashMap() throws Exception {
doTestWithImport("idea/testData/checker/codeFragments/imports/hashMap.kt");
}
}
@TestMetadata("callExpression.kt")
public void testCallExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/callExpression.kt");
public static Test suite() {
TestSuite suite = new TestSuite("CodeFragmentHighlightingTestGenerated");
suite.addTestSuite(CodeFragments.class);
suite.addTestSuite(Imports.class);
return suite;
}
@TestMetadata("contextElementAsStatement.kt")
public void testContextElementAsStatement() throws Exception {
doTest("idea/testData/checker/codeFragments/contextElementAsStatement.kt");
}
@TestMetadata("simpleNameExpression.kt")
public void testSimpleNameExpression() throws Exception {
doTest("idea/testData/checker/codeFragments/simpleNameExpression.kt");
}
@TestMetadata("smartCasts.kt")
public void testSmartCasts() throws Exception {
doTest("idea/testData/checker/codeFragments/smartCasts.kt");
}
}
@@ -46,6 +46,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
doTest("idea/testData/debugger/tinyApp/src/evaluate/arrays.kt");
}
@TestMetadata("classFromAnotherPackage.kt")
public void testClassFromAnotherPackage() throws Exception {
doTest("idea/testData/debugger/tinyApp/src/evaluate/classFromAnotherPackage.kt");
}
@TestMetadata("classObjectVal.kt")
public void testClassObjectVal() throws Exception {
doTest("idea/testData/debugger/tinyApp/src/evaluate/classObjectVal.kt");