Moved import policy into ImportInsertHelper

This commit is contained in:
Valentin Kipyatkov
2015-01-27 14:14:54 +03:00
parent 952c64f947
commit 09a99ae605
15 changed files with 55 additions and 45 deletions
@@ -160,7 +160,7 @@ public object ShortenReferences {
if (canShortenNow) { if (canShortenNow) {
elementsToShorten.add(element) elementsToShorten.add(element)
} }
else if (target !in failedToImportDescriptors && mayImport(target)) { else if (target !in failedToImportDescriptors && mayImport(target, file)) {
descriptorsToImport.add(target) descriptorsToImport.add(target)
} }
else { else {
@@ -295,29 +295,25 @@ public object ShortenReferences {
?: listOf() ?: listOf()
} }
//TODO: move into ImportInsertHelper private fun mayImport(descriptor: DeclarationDescriptor, file: JetFile): Boolean {
private fun mayImport(descriptor: DeclarationDescriptor): Boolean { if (descriptor !is ClassDescriptor && descriptor !is PackageViewDescriptor) return false
return when (descriptor) { return ImportInsertHelper.getInstance(file.getProject()).mayImportByCodeStyle(descriptor)
is ClassDescriptor -> descriptor.getContainingDeclaration() is PackageFragmentDescriptor
is PackageViewDescriptor -> true //TODO: option
else -> false
}
} }
// this class is needed to optimize imports only when we actually insert any import (optimization) // this class is needed to optimize imports only when we actually insert any import (optimization)
private class ImportInserter(val file: JetFile) { private class ImportInserter(val file: JetFile) {
private var optimizeImports = true private var optimizeImports = true
private val helper = ImportInsertHelper.getInstance(file.getProject())
fun addImport(target: DeclarationDescriptor): ImportInsertHelper.ImportDescriptorResult { fun addImport(target: DeclarationDescriptor): ImportInsertHelper.ImportDescriptorResult {
if (!mayImport(target)) return ImportInsertHelper.ImportDescriptorResult.FAIL
optimizeImports() optimizeImports()
return ImportInsertHelper.INSTANCE.importDescriptor(file, target) return helper.importDescriptor(file, target)
} }
fun optimizeImports(): Boolean { fun optimizeImports(): Boolean {
if (!optimizeImports) return false if (!optimizeImports) return false
optimizeImports = false optimizeImports = false
return ImportInsertHelper.INSTANCE.optimizeImportsOnTheFly(file) return helper.optimizeImportsOnTheFly(file)
} }
} }
} }
@@ -16,27 +16,30 @@
package org.jetbrains.kotlin.idea.quickfix package org.jetbrains.kotlin.idea.quickfix
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetImportDirective import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import com.intellij.openapi.project.Project
import com.intellij.openapi.components.ServiceManager
import kotlin.platform.platformStatic
public trait ImportInsertHelper { public abstract class ImportInsertHelper {
public fun addImportDirectiveIfNeeded(importFqn: FqName, file: JetFile) public abstract fun addImportDirectiveIfNeeded(importFqn: FqName, file: JetFile)
public fun optimizeImportsOnTheFly(file: JetFile): Boolean public abstract fun optimizeImportsOnTheFly(file: JetFile): Boolean
public fun isImportedWithDefault(importPath: ImportPath, contextFile: JetFile): Boolean public abstract fun isImportedWithDefault(importPath: ImportPath, contextFile: JetFile): Boolean
public fun needImport(fqName: FqName, file: JetFile): Boolean = needImport(ImportPath(fqName, false), file) public fun needImport(fqName: FqName, file: JetFile): Boolean = needImport(ImportPath(fqName, false), file)
public fun needImport(importPath: ImportPath, file: JetFile, importDirectives: List<JetImportDirective> = file.getImportDirectives()): Boolean public abstract fun needImport(importPath: ImportPath, file: JetFile, importDirectives: List<JetImportDirective> = file.getImportDirectives()): Boolean
public fun writeImportToFile(importPath: ImportPath, file: JetFile): JetImportDirective public abstract fun writeImportToFile(importPath: ImportPath, file: JetFile): JetImportDirective
public abstract fun mayImportByCodeStyle(descriptor: DeclarationDescriptor): Boolean
public enum class ImportDescriptorResult { public enum class ImportDescriptorResult {
FAIL FAIL
@@ -44,10 +47,11 @@ public trait ImportInsertHelper {
ALREADY_IMPORTED ALREADY_IMPORTED
} }
public fun importDescriptor(file: JetFile, descriptor: DeclarationDescriptor): ImportDescriptorResult public abstract fun importDescriptor(file: JetFile, descriptor: DeclarationDescriptor): ImportDescriptorResult
class object { class object {
public val INSTANCE: ImportInsertHelper [platformStatic]
get() = ServiceManager.getService<ImportInsertHelper>(javaClass<ImportInsertHelper>()) public fun getInstance(project: Project): ImportInsertHelper
= ServiceManager.getService<ImportInsertHelper>(project, javaClass<ImportInsertHelper>())
} }
} }
+1 -1
View File
@@ -142,7 +142,7 @@
<applicationService serviceInterface="org.jetbrains.kotlin.idea.configuration.JetModuleTypeManager" <applicationService serviceInterface="org.jetbrains.kotlin.idea.configuration.JetModuleTypeManager"
serviceImplementation="org.jetbrains.kotlin.idea.JetModuleTypeManagerImpl"/> serviceImplementation="org.jetbrains.kotlin.idea.JetModuleTypeManagerImpl"/>
<applicationService serviceInterface="org.jetbrains.kotlin.idea.quickfix.ImportInsertHelper" <projectService serviceInterface="org.jetbrains.kotlin.idea.quickfix.ImportInsertHelper"
serviceImplementation="org.jetbrains.kotlin.idea.quickfix.ImportInsertHelperImpl"/> serviceImplementation="org.jetbrains.kotlin.idea.quickfix.ImportInsertHelperImpl"/>
<applicationService serviceInterface="org.jetbrains.kotlin.psi.KotlinDeclarationNavigationPolicy" <applicationService serviceInterface="org.jetbrains.kotlin.psi.KotlinDeclarationNavigationPolicy"
@@ -115,7 +115,7 @@ public class JetAddImportAction(
override fun run() { override fun run() {
ApplicationManager.getApplication().runWriteAction { ApplicationManager.getApplication().runWriteAction {
val file = element.getContainingFile() as JetFile val file = element.getContainingFile() as JetFile
ImportInsertHelper.INSTANCE.writeImportToFile(ImportPath(selectedImport, false), file) ImportInsertHelper.getInstance(project).writeImportToFile(ImportPath(selectedImport, false), file)
} }
} }
}, QuickFixBundle.message("add.import"), null) }, QuickFixBundle.message("add.import"), null)
@@ -223,7 +223,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
return Collections.emptyList() return Collections.emptyList()
} }
return referenceData.map { return referenceData.map {
if (ImportInsertHelper.INSTANCE.needImport(it.fqName, file)) { if (ImportInsertHelper.getInstance(file.getProject()).needImport(it.fqName, file)) {
val referenceExpression = findReference(it, file, bounds) val referenceExpression = findReference(it, file, bounds)
if (referenceExpression != null) createReferenceToRestoreData(referenceExpression, it.fqName) else null if (referenceExpression != null) createReferenceToRestoreData(referenceExpression, it.fqName) else null
} }
@@ -287,7 +287,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: JetFile) { private fun restoreReferences(referencesToRestore: Collection<ReferenceToRestoreData>, file: JetFile) {
for ((referenceExpression, fqName, shouldLengthen) in referencesToRestore) { for ((referenceExpression, fqName, shouldLengthen) in referencesToRestore) {
if (!shouldLengthen) { if (!shouldLengthen) {
ImportInsertHelper.INSTANCE.addImportDirectiveIfNeeded(fqName, file) ImportInsertHelper.getInstance(file.getProject()).addImportDirectiveIfNeeded(fqName, file)
} }
else { else {
//TODO: try to shorten reference after (sometimes is possible), need shorten reference to support all relevant cases //TODO: try to shorten reference after (sometimes is possible), need shorten reference to support all relevant cases
@@ -150,7 +150,7 @@ private class JetDeclarationRemotenessWeigher(private val file: JetFile) : Looku
val fqName = importPath.fqnPart() val fqName = importPath.fqnPart()
return when { return when {
JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Weight.notToBeUsedInKotlin JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Weight.notToBeUsedInKotlin
ImportInsertHelper.INSTANCE.isImportedWithDefault(importPath, file) -> Weight.kotlinDefaultImport ImportInsertHelper.getInstance(file.getProject()).isImportedWithDefault(importPath, file) -> Weight.kotlinDefaultImport
importCache.isImportedWithPreciseImport(fqName) -> Weight.preciseImport importCache.isImportedWithPreciseImport(fqName) -> Weight.preciseImport
importCache.isImportedWithAllUnderImport(fqName) -> Weight.allUnderImport importCache.isImportedWithAllUnderImport(fqName) -> Weight.allUnderImport
importCache.hasPreciseImportFromPackage(fqName.parent()) -> Weight.hasImportFromSamePackage importCache.hasPreciseImportFromPackage(fqName.parent()) -> Weight.hasImportFromSamePackage
@@ -77,7 +77,7 @@ public abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) { if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
ApplicationManager.getApplication()?.runWriteAction { ApplicationManager.getApplication()?.runWriteAction {
ImportInsertHelper.INSTANCE.addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file) ImportInsertHelper.getInstance(context.getProject()).addImportDirectiveIfNeeded(DescriptorUtils.getFqNameSafe(descriptor), file)
} }
} }
} }
@@ -42,6 +42,7 @@ public class JetCodeStyleSettings extends CustomCodeStyleSettings {
public boolean LBRACE_ON_NEXT_LINE = false; public boolean LBRACE_ON_NEXT_LINE = false;
public boolean PREFER_ALL_UNDER_IMPORTS = false; public boolean PREFER_ALL_UNDER_IMPORTS = false;
public boolean IMPORT_PACKAGES = true;
public static JetCodeStyleSettings getInstance(Project project) { public static JetCodeStyleSettings getInstance(Project project) {
return CodeStyleSettingsManager.getSettings(project).getCustomSettings(JetCodeStyleSettings.class); return CodeStyleSettingsManager.getSettings(project).getCustomSettings(JetCodeStyleSettings.class);
@@ -44,6 +44,7 @@ public class KotlinImportOptimizer() : ImportOptimizer {
val directivesAfterCurrent = ArrayList(jetFile.getImportDirectives()) val directivesAfterCurrent = ArrayList(jetFile.getImportDirectives())
ApplicationManager.getApplication()!!.runWriteAction(Runnable { ApplicationManager.getApplication()!!.runWriteAction(Runnable {
val importInsertHelper = ImportInsertHelper.getInstance(file.getProject())
// Remove only unnecessary imports // Remove only unnecessary imports
for (anImport in directives) { for (anImport in directives) {
directivesAfterCurrent.remove(anImport) directivesAfterCurrent.remove(anImport)
@@ -54,8 +55,8 @@ public class KotlinImportOptimizer() : ImportOptimizer {
} }
if (isUseful(importPath, usedQualifiedNames) if (isUseful(importPath, usedQualifiedNames)
&& ImportInsertHelper.INSTANCE.needImport(importPath, jetFile, directivesBeforeCurrent) && importInsertHelper.needImport(importPath, jetFile, directivesBeforeCurrent)
&& ImportInsertHelper.INSTANCE.needImport(importPath, jetFile, directivesAfterCurrent) && importInsertHelper.needImport(importPath, jetFile, directivesAfterCurrent)
) { ) {
directivesBeforeCurrent.add(anImport) directivesBeforeCurrent.add(anImport)
} }
@@ -50,8 +50,10 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.idea.quickfix.ImportInsertHelper.ImportDescriptorResult import org.jetbrains.kotlin.idea.quickfix.ImportInsertHelper.ImportDescriptorResult
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import com.intellij.openapi.project.Project
public class ImportInsertHelperImpl : ImportInsertHelper { public class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() {
/** /**
* Add import directive into the PSI tree for the given package. * Add import directive into the PSI tree for the given package.
* *
@@ -70,7 +72,7 @@ public class ImportInsertHelperImpl : ImportInsertHelper {
override fun optimizeImportsOnTheFly(file: JetFile): Boolean { override fun optimizeImportsOnTheFly(file: JetFile): Boolean {
if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) {
OptimizeImportsProcessor(file.getProject(), file).runWithoutProgress() OptimizeImportsProcessor(project, file).runWithoutProgress()
return true return true
} }
else { else {
@@ -79,7 +81,7 @@ public class ImportInsertHelperImpl : ImportInsertHelper {
} }
override fun writeImportToFile(importPath: ImportPath, file: JetFile): JetImportDirective { override fun writeImportToFile(importPath: ImportPath, file: JetFile): JetImportDirective {
val psiFactory = JetPsiFactory(file.getProject()) val psiFactory = JetPsiFactory(project)
if (file is JetCodeFragment) { if (file is JetCodeFragment) {
val newDirective = psiFactory.createImportDirective(importPath) val newDirective = psiFactory.createImportDirective(importPath)
file.addImportsFromString(newDirective.getText()) file.addImportsFromString(newDirective.getText())
@@ -153,6 +155,15 @@ public class ImportInsertHelperImpl : ImportInsertHelper {
return true return true
} }
override fun mayImportByCodeStyle(descriptor: DeclarationDescriptor): Boolean {
val importable = descriptor.getImportableDescriptor()
return when (importable) {
is ClassDescriptor -> importable.getContainingDeclaration() is PackageFragmentDescriptor // do not import nested classes
is PackageViewDescriptor -> JetCodeStyleSettings.getInstance(project).IMPORT_PACKAGES
else -> true
}
}
override fun importDescriptor(file: JetFile, descriptor: DeclarationDescriptor): ImportDescriptorResult { override fun importDescriptor(file: JetFile, descriptor: DeclarationDescriptor): ImportDescriptorResult {
return Importer(file).importDescriptor(descriptor) return Importer(file).importDescriptor(descriptor)
} }
@@ -161,13 +172,10 @@ public class ImportInsertHelperImpl : ImportInsertHelper {
private val file: JetFile private val file: JetFile
) { ) {
private val resolutionFacade = file.getResolutionFacade() private val resolutionFacade = file.getResolutionFacade()
private val preferAllUnderImports = JetCodeStyleSettings.getInstance(file.getProject()).PREFER_ALL_UNDER_IMPORTS private val preferAllUnderImports = JetCodeStyleSettings.getInstance(project).PREFER_ALL_UNDER_IMPORTS
fun importDescriptor(descriptor: DeclarationDescriptor): ImportDescriptorResult { fun importDescriptor(descriptor: DeclarationDescriptor): ImportDescriptorResult {
val target = if (DescriptorUtils.isClassObject(descriptor)) // references to class object are treated as ones to its owner class val target = descriptor.getImportableDescriptor()
descriptor.getContainingDeclaration() as? ClassDescriptor ?: return ImportDescriptorResult.FAIL
else
descriptor
val name = target.getName() val name = target.getName()
val topLevelScope = resolutionFacade.getFileTopLevelScope(file) val topLevelScope = resolutionFacade.getFileTopLevelScope(file)
@@ -212,7 +212,7 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
is MoveRenameUsageInfoForExtension -> { is MoveRenameUsageInfoForExtension -> {
val element = counterpart(usage.getReferencedElement()!!) val element = counterpart(usage.getReferencedElement()!!)
val file = with(usage) { if (addImportToOriginalFile) originalFile else counterpart(originalFile) } as JetFile val file = with(usage) { if (addImportToOriginalFile) originalFile else counterpart(originalFile) } as JetFile
ImportInsertHelper.INSTANCE.addImportDirectiveIfNeeded(element.getKotlinFqName()!!, file) ImportInsertHelper.getInstance(element.getProject()).addImportDirectiveIfNeeded(element.getKotlinFqName()!!, file)
} }
is MoveRenameUsageInfo -> { is MoveRenameUsageInfo -> {
@@ -57,7 +57,7 @@ public abstract class AbstractAddImportTest : AbstractImportsTest() {
error("Multiple descriptors found:\n " + descriptors.map { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) }.joinToString("\n ")) error("Multiple descriptors found:\n " + descriptors.map { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) }.joinToString("\n "))
else -> { else -> {
val success = ImportInsertHelper.INSTANCE.importDescriptor(file, descriptors.single()) != ImportInsertHelper.ImportDescriptorResult.FAIL val success = ImportInsertHelper.getInstance(getProject()).importDescriptor(file, descriptors.single()) != ImportInsertHelper.ImportDescriptorResult.FAIL
if (!success) { if (!success) {
val document = PsiDocumentManager.getInstance(getProject()).getDocument(file) val document = PsiDocumentManager.getInstance(getProject()).getDocument(file)
document.replaceString(0, document.getTextLength(), "Failed to add import") document.replaceString(0, document.getTextLength(), "Failed to add import")
@@ -50,7 +50,7 @@ public abstract class AbstractCodeFragmentHighlightingTest : AbstractJetPsiCheck
ApplicationManager.getApplication()?.runWriteAction { ApplicationManager.getApplication()?.runWriteAction {
val fileText = FileUtil.loadFile(File(filePath), true) val fileText = FileUtil.loadFile(File(filePath), true)
InTextDirectivesUtils.findListWithPrefixes(fileText, "// IMPORT: ").forEach { InTextDirectivesUtils.findListWithPrefixes(fileText, "// IMPORT: ").forEach {
ImportInsertHelper.INSTANCE.addImportDirectiveIfNeeded(FqName(it), (myFixture.getFile() as JetFile)) ImportInsertHelper.getInstance(getProject()).addImportDirectiveIfNeeded(FqName(it), (myFixture.getFile() as JetFile))
} }
} }
@@ -33,7 +33,7 @@ public class OptimizeImportsOnFlyTest extends LightDaemonAnalyzerTestCase {
ApplicationManager.getApplication().runWriteAction(new Runnable() { ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override @Override
public void run() { public void run() {
ImportInsertHelper.OBJECT$.getINSTANCE().addImportDirectiveIfNeeded(new FqName("java.util.HashSet"), (JetFile) getFile()); ImportInsertHelper.getInstance(getProject()).addImportDirectiveIfNeeded(new FqName("java.util.HashSet"), (JetFile) getFile());
} }
}); });
} }
@@ -46,7 +46,7 @@ public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
ApplicationManager.getApplication().runWriteAction(new Runnable() { ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override @Override
public void run() { public void run() {
ImportInsertHelper.OBJECT$.getINSTANCE().addImportDirectiveIfNeeded(new FqName("java.util.ArrayList"), (JetFile) getFile()); ImportInsertHelper.getInstance(getProject()).addImportDirectiveIfNeeded(new FqName("java.util.ArrayList"), (JetFile) getFile());
} }
}); });
@@ -58,7 +58,7 @@ public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
ApplicationManager.getApplication().runWriteAction(new Runnable() { ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override @Override
public void run() { public void run() {
ImportInsertHelper.OBJECT$.getINSTANCE().addImportDirectiveIfNeeded(new FqName("java.util.ArrayList"), (JetFile) getFile()); ImportInsertHelper.getInstance(getProject()).addImportDirectiveIfNeeded(new FqName("java.util.ArrayList"), (JetFile) getFile());
} }
}); });
@@ -70,7 +70,7 @@ public class ImportClassHelperTest extends LightDaemonAnalyzerTestCase {
ApplicationManager.getApplication().runWriteAction(new Runnable() { ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override @Override
public void run() { public void run() {
ImportInsertHelper.OBJECT$.getINSTANCE().addImportDirectiveIfNeeded(new FqName(importString), (JetFile) getFile()); ImportInsertHelper.getInstance(getProject()).addImportDirectiveIfNeeded(new FqName(importString), (JetFile) getFile());
} }
}); });
checkResultByFile(getTestName(false) + ".kt.after"); checkResultByFile(getTestName(false) + ".kt.after");