From f6162dc726880a934c2465c217fdd6601c074774 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Thu, 5 Mar 2015 16:36:43 +0300 Subject: [PATCH] JetCodeFragment: increment modification count after inserting import --- .../kotlin/psi/JetBlockCodeFragment.kt | 3 +- .../jetbrains/kotlin/psi/JetCodeFragment.kt | 41 ++++++++++++++----- .../kotlin/psi/JetExpressionCodeFragment.kt | 3 +- .../org/jetbrains/kotlin/psi/JetPsiFactory.kt | 4 +- .../kotlin/psi/JetTypeCodeFragment.java | 2 +- .../idea/debugger/KotlinEditorTextProvider.kt | 2 +- .../evaluate/KotlinCodeFragmentFactory.kt | 5 +-- .../evaluate/KotlinEvaluationBuilder.kt | 4 -- .../idea/util/ImportInsertHelperImpl.kt | 5 ++- ...AbstractEditorForEvaluateExpressionTest.kt | 4 +- .../AbstractKotlinEvaluateExpressionTest.kt | 6 +-- 11 files changed, 46 insertions(+), 33 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetBlockCodeFragment.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetBlockCodeFragment.kt index 85f1c283dc4..4150f7a04f3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetBlockCodeFragment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetBlockCodeFragment.kt @@ -24,8 +24,9 @@ public class JetBlockCodeFragment( project: Project, name: String, text: CharSequence, + imports: String?, context: PsiElement? -) : JetCodeFragment(project, name, text, JetNodeTypes.BLOCK_CODE_FRAGMENT, context) { +) : JetCodeFragment(project, name, text, imports, JetNodeTypes.BLOCK_CODE_FRAGMENT, context) { override fun getContentElement() = findChildByClass(javaClass()) ?: throw IllegalStateException("Block expression should be parsed for BlockCodeFragment") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt index fae9e8d4d0d..5e82ddb1628 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt @@ -26,24 +26,28 @@ import com.intellij.testFramework.LightVirtualFile import org.jetbrains.kotlin.idea.JetFileType import java.util.HashSet import com.intellij.openapi.util.Key +import com.intellij.psi.impl.PsiModificationTrackerImpl +import com.intellij.psi.util.PsiModificationTracker import org.jetbrains.kotlin.types.JetType +import java.util.LinkedHashSet public abstract class JetCodeFragment( private val _project: Project, name: String, text: CharSequence, + imports: String?, // Should be separated by JetCodeFragment.IMPORT_SEPARATOR elementType: IElementType, private val context: PsiElement? ): JetFile((PsiManager.getInstance(_project) as PsiManagerEx).getFileManager().createFileViewProvider(LightVirtualFile(name, JetFileType.INSTANCE, text), true), false), JavaCodeFragment { private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider - private var myImports = HashSet(); + private var myImports = LinkedHashSet(); { getViewProvider().forceCachedPsi(this) init(TokenType.CODE_FRAGMENT, elementType) if (context != null) { - addImportsFromString(getImportsForElement(context)) + initImports(context, imports) } } @@ -98,6 +102,11 @@ public abstract class JetCodeFragment( override fun addImportsFromString(imports: String?) { if (imports == null || imports.isEmpty()) return + // We should increment modification tracker after inserting import in code fragment to invalidate resolve caches. + // Without this modification references with new import won't be resolved without any modification in code fragment. + // Also shorten references won't work. + (PsiModificationTracker.SERVICE.getInstance(getProject()) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter() + myImports.addAll(imports.split(IMPORT_SEPARATOR)) } @@ -119,17 +128,27 @@ public abstract class JetCodeFragment( return true } + private fun initImports(context: PsiElement, imports: String?) { + val containingFile = context.getContainingFile() + if (containingFile !is JetFile) return + + val importListForContextElement = containingFile.getImportList() + if (importListForContextElement != null) { + myImports.addAll(importListForContextElement.getImports().map { it.getText() }) + } + + val packageName = containingFile.getPackageDirective()?.getFqName()?.asString() + if (packageName != null && packageName.isNotEmpty()) { + myImports.add("import $packageName.*") + } + + if (imports != null && !imports.isEmpty()) { + myImports.addAll(imports.split(IMPORT_SEPARATOR)) + } + } + class object { public val IMPORT_SEPARATOR: String = "," public val RUNTIME_TYPE_EVALUATOR: Key> = Key.create("RUNTIME_TYPE_EVALUATOR") - - public fun getImportsForElement(elementAtCaret: PsiElement): String { - val containingFile = elementAtCaret.getContainingFile() - if (containingFile !is JetFile) return "" - - return containingFile.getImportList()?.getImports() - ?.map { it.getText() } - ?.join(JetCodeFragment.IMPORT_SEPARATOR) ?: "" - } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetExpressionCodeFragment.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetExpressionCodeFragment.kt index 180d6ed85f6..408ee445d87 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetExpressionCodeFragment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetExpressionCodeFragment.kt @@ -24,8 +24,9 @@ public class JetExpressionCodeFragment( project: Project, name: String, text: CharSequence, + imports: String?, context: PsiElement? -) : JetCodeFragment(project, name, text, JetNodeTypes.EXPRESSION_CODE_FRAGMENT, context) { +) : JetCodeFragment(project, name, text, imports, JetNodeTypes.EXPRESSION_CODE_FRAGMENT, context) { override fun getContentElement() = findChildByClass(javaClass()) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index 50241abb8d5..d5f0af231ea 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -323,11 +323,11 @@ public class JetPsiFactory(private val project: Project) { } public fun createExpressionCodeFragment(text: String, context: PsiElement?): JetExpressionCodeFragment { - return JetExpressionCodeFragment(project, "fragment.kt", text, context) + return JetExpressionCodeFragment(project, "fragment.kt", text, null, context) } public fun createBlockCodeFragment(text: String, context: PsiElement?): JetBlockCodeFragment { - return JetBlockCodeFragment(project, "fragment.kt", text, context) + return JetBlockCodeFragment(project, "fragment.kt", text, null, context) } public fun createReturn(text: String): JetReturnExpression { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetTypeCodeFragment.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetTypeCodeFragment.java index 4803a043e7c..b21a96eb44d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetTypeCodeFragment.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetTypeCodeFragment.java @@ -25,7 +25,7 @@ import org.jetbrains.kotlin.types.JetType; public class JetTypeCodeFragment extends JetCodeFragment { public JetTypeCodeFragment(Project project, String name, CharSequence text, PsiElement context) { - super(project, name, text, JetNodeTypes.TYPE_CODE_FRAGMENT, context); + super(project, name, text, null, JetNodeTypes.TYPE_CODE_FRAGMENT, context); } @Nullable diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt index b4a2c192466..bbfda77cd21 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinEditorTextProvider.kt @@ -43,7 +43,7 @@ import org.jetbrains.kotlin.psi.JetArrayAccessExpression class KotlinEditorTextProvider : EditorTextProvider { override fun getEditorText(elementAtCaret: PsiElement): TextWithImports? { val expression = findExpressionInner(elementAtCaret, true) - return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression?.getText() ?: "", JetCodeFragment.getImportsForElement(elementAtCaret), JetFileType.INSTANCE) + return TextWithImportsImpl(CodeFragmentKind.EXPRESSION, expression?.getText() ?: "", "", JetFileType.INSTANCE) } override fun findExpression(elementAtCaret: PsiElement, allowMethodCalls: Boolean): Pair? { diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt index 30e2e23577c..9b738f85384 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinCodeFragmentFactory.kt @@ -40,12 +40,11 @@ import com.intellij.openapi.progress.ProgressManager class KotlinCodeFragmentFactory: CodeFragmentFactory() { override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { val codeFragment = if (item.getKind() == CodeFragmentKind.EXPRESSION) { - JetExpressionCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context)) + JetExpressionCodeFragment(project, "fragment.kt", item.getText(), item.getImports(), getContextElement(context)) } else { - JetBlockCodeFragment(project, "fragment.kt", item.getText(), getContextElement(context)) + JetBlockCodeFragment(project, "fragment.kt", item.getText(), item.getImports(), getContextElement(context)) } - codeFragment.addImportsFromString(item.getImports()) codeFragment.putCopyableUserData(JetCodeFragment.RUNTIME_TYPE_EVALUATOR, { (expression: JetExpression): JetType? -> diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt index 9cdf1337e62..1461c6d36f5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -97,10 +97,6 @@ object KotlinEvaluationBuilder: EvaluatorBuilder { throw EvaluateExceptionUtil.createEvaluateException("Couldn't evaluate kotlin expression in this context") } - val packageName = file.getPackageDirective()?.getFqName()?.asString() - if (packageName != null && packageName.isNotEmpty()) { - codeFragment.addImportsFromString("import $packageName.*") - } return ExpressionEvaluatorImpl(KotlinEvaluator(codeFragment as JetCodeFragment, position)) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt index 9820d694c9b..013f9145a7b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt @@ -179,7 +179,10 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert return ImportDescriptorResult.FAIL } - val imports = file.getImportDirectives() + val imports = if (file is JetCodeFragment) + file.importsAsImportList()?.getImports() ?: listOf() + else + file.getImportDirectives() //TODO: is that correct? What if function is imported and we need to import class? if (imports.any { it.getImportedName() == name.asString() }) return ImportDescriptorResult.FAIL diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractEditorForEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractEditorForEvaluateExpressionTest.kt index 4a94ab3dea6..b2954eb8cfc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractEditorForEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractEditorForEvaluateExpressionTest.kt @@ -66,8 +66,6 @@ public abstract class AbstractCodeFragmentHighlightingTest : AbstractJetPsiCheck .processImportReference(importDirective, scope, scope, null, BindingTraceContext(), LookupMode.EVERYTHING) .singleOrNull() ?: error("Could not resolve descriptor to import: $it") ImportInsertHelper.getInstance(getProject()).importDescriptor(file, descriptor) - //TODO: it's a hack! we need to discuss it - (PsiModificationTracker.SERVICE.getInstance(getProject()) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter() } } @@ -90,7 +88,7 @@ public abstract class AbstractCodeFragmentCompletionHandlerTest : AbstractComple super.doTest(testPath) val fragment = myFixture.getFile() as JetCodeFragment - val importList = fragment.getImportList() + val importList = fragment.importsAsImportList() val fragmentAfterFile = File(testPath + ".after.imports") if (importList != null && fragmentAfterFile.exists()) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index 6bc6e0bf614..34c5c1b6fcd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -331,11 +331,7 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestB try { val evaluator = - EvaluatorBuilderImpl.build(TextWithImportsImpl( - codeFragmentKind, - text, - JetCodeFragment.getImportsForElement(contextElement), - JetFileType.INSTANCE), + EvaluatorBuilderImpl.build(TextWithImportsImpl(codeFragmentKind, text, "", JetFileType.INSTANCE), contextElement, sourcePosition)