New optimize imports algorithm supports *-imports code style
This commit is contained in:
@@ -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>()
|
||||
}
|
||||
+10
@@ -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>) {
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// ALL_UNDER_IMPORTS: true
|
||||
package ppp
|
||||
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
|
||||
class ArrayList
|
||||
|
||||
fun foo(p: HashSet<ArrayList>) {
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// ALL_UNDER_IMPORTS: true
|
||||
package ppp
|
||||
|
||||
import java.util.HashSet
|
||||
|
||||
class ArrayList
|
||||
|
||||
fun foo(p: HashSet<ArrayList>) {
|
||||
}
|
||||
+9
@@ -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) {
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user