New optimize imports algorithm supports *-imports code style

This commit is contained in:
Valentin Kipyatkov
2015-02-17 14:20:39 +03:00
parent 2e3ed6b66f
commit 8c034d4ac7
16 changed files with 230 additions and 6 deletions
@@ -29,6 +29,7 @@ public abstract class ImportInsertHelper {
public abstract fun optimizeImportsOnTheFly(file: JetFile): Boolean
/*TODO: implementation is not quite correct*/
public abstract fun isImportedWithDefault(importPath: ImportPath, contextFile: JetFile): Boolean
public abstract fun mayImportByCodeStyle(descriptor: DeclarationDescriptor): Boolean
@@ -24,13 +24,15 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import java.util.*
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.descriptors.*
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.*
public class KotlinImportOptimizer() : ImportOptimizer {
@@ -68,17 +70,76 @@ public class KotlinImportOptimizer() : ImportOptimizer {
val descriptorsToImport = detectDescriptorsToImport()
val importsToGenerate = ArrayList<ImportPath>()
val importsToGenerate = HashSet<ImportPath>()
if (codeStyleSettings.PREFER_ALL_UNDER_IMPORTS) {
//TODO
//TODO: don't forget that packages may not be imported by *
val packageNames = HashMap<FqName, Int>() // package name to symbols to import count
fun incUseCount(packageName: FqName): Int {
val count = packageNames.getOrPut(packageName, { 0 }) + 1
packageNames.put(packageName, count)
return count
}
fun decUseCount(packageName: FqName): Int {
val count = packageNames[packageName]!! - 1
if (count > 0) {
packageNames[packageName] = count
}
else {
packageNames.remove(packageName)
}
return count
}
val classNames = HashSet<FqName>()
val builder = StringBuilder()
builder.append("package ").append(IdeDescriptorRenderers.SOURCE_CODE.renderFqName(currentPackageName)).append("\n")
for (descriptor in descriptorsToImport) {
val fqName = descriptor.importableFqNameSafe
val parentFqName = fqName.parent()
if (descriptor is PackageViewDescriptor || parentFqName.isRoot()) {
importsToGenerate.add(ImportPath(fqName, false))
}
else {
val useCount = incUseCount(parentFqName)
if (descriptor is ClassifierDescriptor) {
classNames.add(fqName)
}
if (parentFqName == currentPackageName) continue // in current package
if (importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file)) continue
if (useCount == 1) {
builder.append("import ").append(IdeDescriptorRenderers.SOURCE_CODE.renderFqName(parentFqName)).append(".*\n")
importsToGenerate.add(ImportPath(parentFqName, true))
}
}
}
// now check that all classes are really imported
val fileWithImports = JetPsiFactory(file).createAnalyzableFile("Dummy.kt", builder.toString(), file)
val scope = fileWithImports.getResolutionFacade().getFileTopLevelScope(fileWithImports)
for (fqName in classNames) {
if (scope.getClassifier(fqName.shortName())?.importableFqNameSafe != fqName) {
// add explicit import if failed to import with * (or from current package)
importsToGenerate.add(ImportPath(fqName, false))
val packageName = fqName.parent()
if (decUseCount(packageName) == 0) { // all under import is not really needed
importsToGenerate.remove(ImportPath(packageName, true))
}
}
}
}
else {
for (descriptor in descriptorsToImport) {
val fqName = descriptor.importableFqNameSafe
if (descriptor !is PackageViewDescriptor && fqName.parent() == currentPackageName) continue
if (descriptor !is PackageViewDescriptor && fqName.parent() == currentPackageName) continue // in current package
val importPath = ImportPath(fqName, false)
if (importInsertHelper.isImportedWithDefault(importPath, file)/*TODO: implementation is not quite correct*/) continue
if (importInsertHelper.isImportedWithDefault(importPath, file)) continue
importsToGenerate.add(importPath)
}
}
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.HashMap
import java.util.ArrayList
import java.sql.Date
fun foo() {
val v = HashMap<Date, ArrayList<Int>>()
}
@@ -0,0 +1,9 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.sql.Date
import java.util.*
fun foo() {
val v = HashMap<Date, ArrayList<Int>>()
}
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.HashMap
import java.sql.Date
import java.sql.Driver
fun foo() {
val v = HashMap<Date, Driver>()
}
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.sql.*
import java.sql.Date
import java.util.*
fun foo() {
val v = HashMap<Date, Driver>()
}
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.ArrayList
import java.util.HashSet
class ArrayList
fun foo(p: HashSet<ArrayList>) {
}
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.*
import java.util.ArrayList
class ArrayList
fun foo(p: HashSet<ArrayList>) {
}
@@ -0,0 +1,9 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.HashSet
class ArrayList
fun foo(p: HashSet<ArrayList>) {
}
@@ -0,0 +1,9 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.*
class ArrayList
fun foo(p: HashSet<ArrayList>) {
}
@@ -0,0 +1,8 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.sql.Array
import java.sql.Date
fun foo(a: Array, d: Date) {
}
@@ -0,0 +1,8 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.sql.*
import java.sql.Array
fun foo(a: Array, d: Date) {
}
@@ -0,0 +1,3 @@
package ppp
class C
@@ -0,0 +1,10 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.util.HashMap
import java.util.ArrayList
import java.io.File
fun foo(c: C) {
val v = HashMap<File, ArrayList<Int>>()
}
@@ -0,0 +1,9 @@
// ALL_UNDER_IMPORTS: true
package ppp
import java.io.*
import java.util.*
fun foo(c: C) {
val v = HashMap<File, ArrayList<Int>>()
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.imports;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.InnerTestClasses;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.JetTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
@@ -29,6 +30,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("idea/testData/editor/optimizeImports")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({OptimizeImportsTestGenerated.AllUnderImports.class})
@RunWith(JUnit3RunnerWithInners.class)
public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
public void testAllFilesPresentInOptimizeImports() throws Exception {
@@ -172,4 +174,49 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/WithAliases.kt");
doTest(fileName);
}
@TestMetadata("idea/testData/editor/optimizeImports/allUnderImports")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AllUnderImports extends AbstractOptimizeImportsTest {
public void testAllFilesPresentInAllUnderImports() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/editor/optimizeImports/allUnderImports"), Pattern.compile("^([^\\.]+)\\.kt$"), true);
}
@TestMetadata("ClassNameConflict.kt")
public void testClassNameConflict() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/ClassNameConflict.kt");
doTest(fileName);
}
@TestMetadata("ClassNameConflict2.kt")
public void testClassNameConflict2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/ClassNameConflict2.kt");
doTest(fileName);
}
@TestMetadata("ClassNameConflictWithCurrentPackage.kt")
public void testClassNameConflictWithCurrentPackage() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/ClassNameConflictWithCurrentPackage.kt");
doTest(fileName);
}
@TestMetadata("ClassNameConflictWithCurrentPackage2.kt")
public void testClassNameConflictWithCurrentPackage2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/ClassNameConflictWithCurrentPackage2.kt");
doTest(fileName);
}
@TestMetadata("ClassNameConflictWithDefault.kt")
public void testClassNameConflictWithDefault() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/ClassNameConflictWithDefault.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/allUnderImports/Simple.kt");
doTest(fileName);
}
}
}