diff --git a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt index eeabe52a99d..77495c377c5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt @@ -16,23 +16,33 @@ package org.jetbrains.kotlin.idea.imports +import com.google.common.collect.HashMultimap import com.intellij.lang.ImportOptimizer import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.progress.ProgressIndicatorProvider import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.name.* -import org.jetbrains.kotlin.idea.references.JetReference -import java.util.* -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.formatter.JetCodeStyleSettings -import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.idea.caches.resolve.* -import org.jetbrains.kotlin.resolve.scopes.* -import org.jetbrains.kotlin.idea.util.* +import org.jetbrains.kotlin.idea.references.JetReference +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.ImportInsertHelper +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import java.util.ArrayList +import java.util.HashMap +import java.util.HashSet public class KotlinImportOptimizer() : ImportOptimizer { @@ -72,7 +82,7 @@ public class KotlinImportOptimizer() : ImportOptimizer { val importsToGenerate = HashSet() - val descriptorsByPackages = HashMap>() + val descriptorsByPackages = HashMultimap.create() for (descriptor in descriptorsToImport) { val fqName = descriptor.importableFqNameSafe val parentFqName = fqName.parent() @@ -80,21 +90,21 @@ public class KotlinImportOptimizer() : ImportOptimizer { importsToGenerate.add(ImportPath(fqName, false)) } else { - descriptorsByPackages.getOrPut(parentFqName, { ArrayList() }).add(descriptor) + descriptorsByPackages.put(parentFqName, descriptor) } } - val builder = StringBuilder() - builder.append("package ").append(IdeDescriptorRenderers.SOURCE_CODE.renderFqName(currentPackageName)).append("\n") - val classNamesToCheck = HashSet() - for ((packageName, descriptors) in descriptorsByPackages) { + fun isImportedByDefault(fqName: FqName) = importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file) + + for (packageName in descriptorsByPackages.keys()) { + val descriptors = descriptorsByPackages[packageName] val fqNames = descriptors.map { it.importableFqNameSafe }.toSet() val explicitImports = packageName != currentPackageName && fqNames.size() < codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT if (explicitImports) { for (fqName in fqNames) { - if (!importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file)) { + if (!isImportedByDefault(fqName)) { importsToGenerate.add(ImportPath(fqName, false)) } } @@ -106,17 +116,20 @@ public class KotlinImportOptimizer() : ImportOptimizer { } } - if (packageName == currentPackageName) continue - if (fqNames.all { fqName -> importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file) }) continue - - builder.append("import ").append(IdeDescriptorRenderers.SOURCE_CODE.renderFqName(packageName)).append(".*\n") - importsToGenerate.add(ImportPath(packageName, true)) + if (packageName != currentPackageName && !fqNames.all(::isImportedByDefault)) { + importsToGenerate.add(ImportPath(packageName, true)) + } } } - // now check that all classes are really imported - val fileWithImports = JetPsiFactory(file).createAnalyzableFile("Dummy.kt", builder.toString(), file) + // now check that there are no conflicts and all classes are really imported + val fileWithImportsText = StringBuilder { + append("package ").append(IdeDescriptorRenderers.SOURCE_CODE.renderFqName(currentPackageName)).append("\n") + importsToGenerate.filter { it.isAllUnder() }.map { "import " + it.getPathStr() }.joinTo(this, "\n") + }.toString() + val fileWithImports = JetPsiFactory(file).createAnalyzableFile("Dummy.kt", fileWithImportsText, file) val scope = fileWithImports.getResolutionFacade().getFileTopLevelScope(fileWithImports) + for (fqName in classNamesToCheck) { if (scope.getClassifier(fqName.shortName())?.importableFqNameSafe != fqName) { // add explicit import if failed to import with * (or from current package) @@ -124,11 +137,11 @@ public class KotlinImportOptimizer() : ImportOptimizer { val packageName = fqName.parent() - val descriptors = descriptorsByPackages[packageName] - descriptors.removeAll(descriptors.filter { it.importableFqNameSafe == fqName }) - descriptorsByPackages[packageName] = descriptors + for (descriptor in descriptorsByPackages[packageName].filter { it.importableFqNameSafe == fqName }) { + descriptorsByPackages.remove(packageName, descriptor) + } - if (descriptors.isEmpty()) { // star import is not really needed + if (descriptorsByPackages[packageName].isEmpty()) { // star import is not really needed importsToGenerate.remove(ImportPath(packageName, true)) } } diff --git a/idea/testData/editor/optimizeImports/MembersInScope.kt b/idea/testData/editor/optimizeImports/MembersInScope.kt index b6a30752884..0c5f555fa3d 100644 --- a/idea/testData/editor/optimizeImports/MembersInScope.kt +++ b/idea/testData/editor/optimizeImports/MembersInScope.kt @@ -7,7 +7,7 @@ class Outer { class Nested class C { - fun foo(p1: Nested, p2: ClassObjectNested, p3: Nested2) { } + fun foo(p1: Nested, p2: ClassObjectNested, p3: Inner) { } inner class Inner inner class Inner2 diff --git a/idea/testData/editor/optimizeImports/MembersInScope.kt.after b/idea/testData/editor/optimizeImports/MembersInScope.kt.after index 7b2574cdd2d..15a4c054225 100644 --- a/idea/testData/editor/optimizeImports/MembersInScope.kt.after +++ b/idea/testData/editor/optimizeImports/MembersInScope.kt.after @@ -4,7 +4,7 @@ class Outer { class Nested class C { - fun foo(p1: Nested, p2: ClassObjectNested, p3: Nested2) { } + fun foo(p1: Nested, p2: ClassObjectNested, p3: Inner) { } inner class Inner inner class Inner2