diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml
index 572ebbc570f..e3a59d2243d 100644
--- a/.idea/inspectionProfiles/idea_default.xml
+++ b/.idea/inspectionProfiles/idea_default.xml
@@ -402,7 +402,6 @@
-
diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JetAddImportAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JetAddImportAction.kt
index 6a8233283b4..b603e919b11 100644
--- a/idea/src/org/jetbrains/kotlin/idea/actions/JetAddImportAction.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/actions/JetAddImportAction.kt
@@ -34,7 +34,16 @@ import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
-import org.jetbrains.kotlin.resolve.ImportPath
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import com.intellij.openapi.module.ModuleUtilCore
+import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
+import org.jetbrains.kotlin.idea.codeInsight.DescriptorToDeclarationUtil
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
+import org.jetbrains.kotlin.idea.imports.importableFqNameSafe
+import org.jetbrains.kotlin.idea.util.ShortenReferences
+import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
/**
* Automatically adds import directive to the file for resolving reference.
@@ -44,32 +53,72 @@ public class JetAddImportAction(
private val project: Project,
private val editor: Editor,
private val element: JetSimpleNameExpression,
- imports: Iterable
+ candidates: Collection
) : QuestionAction {
- private val possibleImports = imports.toList()
+
+ private val module = ModuleUtilCore.findModuleForPsiElement(element)
+
+ private enum class Priority {
+ MODULE
+ PROJECT
+ OTHER
+ }
+
+ private fun detectPriority(descriptor: DeclarationDescriptor): Priority {
+ val declaration = DescriptorToDeclarationUtil.getDeclaration(element.getContainingJetFile(), descriptor)
+ return when {
+ declaration == null -> Priority.OTHER
+ ModuleUtilCore.findModuleForPsiElement(declaration) == module -> Priority.MODULE
+ ProjectRootsUtil.isInProjectSource(declaration) -> Priority.PROJECT
+ else -> Priority.OTHER
+ }
+ }
+
+ private inner class Variant(
+ val fqName: FqName,
+ val descriptors: Collection
+ ) {
+ val priority = descriptors.map { detectPriority(it) }.min()!!
+
+ val descriptorToImport: DeclarationDescriptor
+ get() {
+ if (descriptors.size() == 1) return descriptors.single()
+ return descriptors.sortBy {
+ when (it) {
+ is ClassDescriptor -> 0
+ is PackageViewDescriptor -> 1
+ else -> 2
+ }
+ }.first()
+ }
+ }
+
+ private val variants = candidates
+ .groupBy { DescriptorUtils.getFqNameSafe(it) }
+ .map { Variant(it.key, it.value) }
+ .sortBy { it.priority }
override fun execute(): Boolean {
PsiDocumentManager.getInstance(project).commitAllDocuments()
-
if (!element.isValid()) return false
// TODO: Validate resolution variants. See AddImportAction.execute()
- if (possibleImports.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) {
- addImport(element, project, possibleImports[0])
+ if (variants.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) {
+ addImport(element, project, variants.first())
}
else {
- chooseClassAndImport()
+ chooseCandidateAndImport()
}
return true
}
- protected fun getImportSelectionPopup(): BaseListPopupStep {
- return object : BaseListPopupStep(JetBundle.message("imports.chooser.title"), possibleImports) {
+ protected fun getImportSelectionPopup(): BaseListPopupStep {
+ return object : BaseListPopupStep(JetBundle.message("imports.chooser.title"), variants) {
override fun isAutoSelectionEnabled() = false
- override fun onChosen(selectedValue: FqName?, finalChoice: Boolean): PopupStep? {
+ override fun onChosen(selectedValue: Variant?, finalChoice: Boolean): PopupStep? {
if (selectedValue == null) return null
if (finalChoice) {
@@ -77,7 +126,7 @@ public class JetAddImportAction(
return null
}
- val toExclude = AddImportAction.getAllExcludableStrings(selectedValue.asString())
+ val toExclude = AddImportAction.getAllExcludableStrings(selectedValue.fqName.asString())
return object : BaseListPopupStep(null, toExclude) {
override fun getTextFor(value: String): String {
@@ -93,29 +142,38 @@ public class JetAddImportAction(
}
}
- override fun hasSubstep(selectedValue: FqName?) = true
+ override fun hasSubstep(selectedValue: Variant?) = true
- override fun getTextFor(value: FqName) = value.asString()
+ override fun getTextFor(value: Variant) = value.fqName.asString()
// TODO: change icon
- override fun getIconFor(aValue: FqName) = PlatformIcons.CLASS_ICON
+ override fun getIconFor(aValue: Variant) = PlatformIcons.CLASS_ICON
}
}
- private fun chooseClassAndImport() {
+ private fun chooseCandidateAndImport() {
JBPopupFactory.getInstance().createListPopup(getImportSelectionPopup()).showInBestPositionFor(editor)
}
class object {
- protected fun addImport(element: PsiElement, project: Project, selectedImport: FqName) {
+ protected fun addImport(element: PsiElement, project: Project, selectedVariant: Variant) {
PsiDocumentManager.getInstance(project).commitAllDocuments()
CommandProcessor.getInstance().executeCommand(project, object : Runnable {
override fun run() {
ApplicationManager.getApplication().runWriteAction {
val file = element.getContainingFile() as JetFile
- ImportInsertHelper.getInstance(project).writeImportToFile(ImportPath(selectedImport, false), file)
+ val descriptor = selectedVariant.descriptorToImport
+ // for class or package we use ShortenReferences because we not necessary insert an import but may want to insert partly qualified name
+ if (descriptor is ClassDescriptor || descriptor is PackageViewDescriptor) {
+ val fqName = descriptor.importableFqNameSafe
+ val reference = element.getReference() as JetSimpleNameReference
+ reference.bindToFqName(fqName, JetSimpleNameReference.ShorteningMode.FORCED_SHORTENING)
+ }
+ else {
+ ImportInsertHelper.getInstance(project).importDescriptor(file, descriptor)
+ }
}
}
}, QuickFixBundle.message("add.import"), null)
diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt
index 7d09817db8c..4684ad2933a 100644
--- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt
@@ -43,11 +43,8 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
-import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.utils.addIfNotNull
-import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
-import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.resolve.PossiblyBareType
@@ -61,7 +58,7 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
-import org.jetbrains.kotlin.idea.caches.resolve.KotlinLightClassForDecompiledDeclaration
+import org.jetbrains.kotlin.idea.util.psiClassToDescriptor.psiClassToDescriptor
class TypeInstantiationItems(
val resolutionFacade: ResolutionFacade,
@@ -285,14 +282,9 @@ class TypeInstantiationItems(
override fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit) {
val parameters = ClassInheritorsSearch.SearchParameters(psiClass, inheritorSearchScope, true, true, false, nameFilter)
for (inheritor in ClassInheritorsSearch.search(parameters)) {
- val descriptor = if (inheritor is KotlinLightClass && inheritor !is KotlinLightClassForDecompiledDeclaration) {
- val origin = inheritor.getOrigin() ?: continue
- val declaration = toFromOriginalFileMapper.toSyntheticFile(origin) ?: continue
- resolutionFacade.resolveToDescriptor(declaration)
- }
- else {
- resolutionFacade.get(JavaResolveExtension)(inheritor).first.resolveClass(JavaClassImpl(inheritor))
- } as? ClassDescriptor ?: continue
+ val descriptor = resolutionFacade.psiClassToDescriptor(
+ inheritor,
+ { toFromOriginalFileMapper.toSyntheticFile(it) as JetClassOrObject? }) as? ClassDescriptor ?: continue
if (!visibilityFilter(descriptor)) continue
val hasTypeArgs = descriptor.getTypeConstructor().getParameters().isNotEmpty()
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt
index 8528624073c..44ebcbe5cca 100644
--- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt
@@ -33,8 +33,6 @@ import org.jetbrains.kotlin.psi.JetPsiUtil
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
-import org.jetbrains.kotlin.resolve.ImportPath
-import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.idea.actions.JetAddImportAction
import org.jetbrains.kotlin.idea.caches.JetShortNamesCache
@@ -44,32 +42,27 @@ import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.util.JetPsiHeuristicsUtil
import java.util.ArrayList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import com.intellij.psi.PsiElement
-import org.jetbrains.kotlin.idea.codeInsight.DescriptorToDeclarationUtil
-import com.intellij.openapi.module.ModuleUtilCore
-import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
-import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.diagnostics.Errors
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.kotlin.idea.completion.isVisible
import org.jetbrains.kotlin.utils.CachedValueProperty
+import org.jetbrains.kotlin.idea.util.psiClassToDescriptor.psiClassToDescriptor
/**
* Check possibility and perform fix for unresolved references.
*/
public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction(element), HighPriorityAction {
- private val module = ModuleUtilCore.findModuleForPsiElement(element)
private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(element.getProject()).getModificationCount()
volatile private var anySuggestionFound: Boolean? = null
- private val suggestions: Collection by CachedValueProperty(
+ private val suggestions: Collection by CachedValueProperty(
{
- val fqNames = computeSuggestions(element)
- anySuggestionFound = !fqNames.isEmpty()
- fqNames
+ val descriptors = computeSuggestions(element)
+ anySuggestionFound = !descriptors.isEmpty()
+ descriptors
},
{ PsiModificationTracker.SERVICE.getInstance(element.getProject()).getModificationCount() })
@@ -81,7 +74,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction 1, suggestions.first().asString())
+ val hintText = ShowAutoImportPass.getMessage(suggestions.size() > 1, DescriptorUtils.getFqNameSafe(suggestions.first()).asString())
val project = editor.getProject() ?: return false
HintManager.getInstance().showQuestionHint(editor, hintText, element.getTextOffset(), element.getTextRange()!!.getEndOffset(), createAction(project, editor))
@@ -109,7 +102,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction {
+ private fun computeSuggestions(element: JetSimpleNameExpression): Collection {
if (!element.isValid()) return listOf()
val file = element.getContainingFile() as? JetFile ?: return listOf()
@@ -143,37 +136,32 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction()
+ val result = ArrayList()
val moduleDescriptor = resolutionFacade.findModuleDescriptor(element)
val indicesHelper = KotlinIndicesHelper(file.getProject(), resolutionFacade, bindingContext, searchScope, moduleDescriptor, ::isVisible)
if (!element.isImportDirectiveExpression() && !JetPsiUtil.isSelectorInQualified(element)) {
- result.addAll(getClassNames(referenceName, file, searchScope))
+ result.addAll(getClasses(referenceName, file, searchScope))
result.addAll(getTopLevelCallables(referenceName, element, indicesHelper))
}
result.addAll(getExtensions(referenceName, element, indicesHelper))
return result
- .sortBy { it.priority }
- .map { it.fqName }
}
- private fun getTopLevelCallables(name: String, context: JetExpression, indicesHelper: KotlinIndicesHelper): Collection
+ private fun getTopLevelCallables(name: String, context: JetExpression, indicesHelper: KotlinIndicesHelper): Collection
= indicesHelper.getTopLevelCallablesByName(name, context)
- .map { PrioritizedFqName(it) }
- .toSet()
- private fun getExtensions(name: String, expression: JetSimpleNameExpression, indicesHelper: KotlinIndicesHelper): Collection
+ private fun getExtensions(name: String, expression: JetSimpleNameExpression, indicesHelper: KotlinIndicesHelper): Collection
= indicesHelper.getCallableExtensions({ it == name }, expression)
- .map { PrioritizedFqName(it) }
- .toSet()
- private fun getClassNames(name: String, file: JetFile, searchScope: GlobalSearchScope): Collection
+ private fun getClasses(name: String, file: JetFile, searchScope: GlobalSearchScope): Collection
= getShortNamesCache(file).getClassesByName(name, searchScope)
.filter { JetPsiHeuristicsUtil.isAccessible(it, file) }
- .map { PrioritizedFqName(it.unwrapped, FqName(it.getQualifiedName()!!)) }
+ .map { element.getResolutionFacade().psiClassToDescriptor(it) }
+ .filterNotNull()
.toSet()
private fun getShortNamesCache(jetFile: JetFile): PsiShortNamesCache {
@@ -184,31 +172,6 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction Priority.OTHER
- ModuleUtilCore.findModuleForPsiElement(declaration) == module -> Priority.MODULE
- ProjectRootsUtil.isInProjectSource(declaration) -> Priority.PROJECT
- else -> Priority.OTHER
- }
-
- return PrioritizedFqName(fqName, priority)
- }
-
- private fun PrioritizedFqName(descriptor: DeclarationDescriptor): PrioritizedFqName {
- return PrioritizedFqName(
- DescriptorToDeclarationUtil.getDeclaration(element.getContainingJetFile(), descriptor),
- DescriptorUtils.getFqNameSafe(descriptor)
- )
- }
-
class object {
private val ERRORS = setOf(Errors.UNRESOLVED_REFERENCE, Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER)
diff --git a/idea/src/org/jetbrains/kotlin/idea/util/psiClassToDescriptor.kt b/idea/src/org/jetbrains/kotlin/idea/util/psiClassToDescriptor.kt
new file mode 100644
index 00000000000..6c5df79fd32
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/util/psiClassToDescriptor.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.util.psiClassToDescriptor
+
+import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
+import com.intellij.psi.PsiClass
+import org.jetbrains.kotlin.psi.JetClassOrObject
+import org.jetbrains.kotlin.asJava.KotlinLightClass
+import org.jetbrains.kotlin.idea.caches.resolve.KotlinLightClassForDecompiledDeclaration
+import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
+import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
+
+public fun ResolutionFacade.psiClassToDescriptor(
+ psiClass: PsiClass,
+ declarationTranslator: (JetClassOrObject) -> JetClassOrObject? = { it }
+): ClassifierDescriptor? {
+ return if (psiClass is KotlinLightClass && psiClass !is KotlinLightClassForDecompiledDeclaration) {
+ val origin = psiClass.getOrigin ()?: return null
+ val declaration = declarationTranslator(origin) ?: return null
+ resolveToDescriptor(declaration)
+ } else {
+ get(JavaResolveExtension)(psiClass).first.resolveClass(JavaClassImpl(psiClass))
+ } as? ClassifierDescriptor
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/autoImports/nestedClass.after.kt b/idea/testData/quickfix/autoImports/nestedClass.after.kt
new file mode 100644
index 00000000000..bf4250525d2
--- /dev/null
+++ b/idea/testData/quickfix/autoImports/nestedClass.after.kt
@@ -0,0 +1,6 @@
+// "Import" "true"
+// ERROR: Unresolved reference: Nested
+
+import test.Test
+
+val a = Test.Nested
diff --git a/idea/testData/quickfix/autoImports/nestedClass.before.Main.kt b/idea/testData/quickfix/autoImports/nestedClass.before.Main.kt
new file mode 100644
index 00000000000..e4d6cfad927
--- /dev/null
+++ b/idea/testData/quickfix/autoImports/nestedClass.before.Main.kt
@@ -0,0 +1,4 @@
+// "Import" "true"
+// ERROR: Unresolved reference: Nested
+
+val a = Nested
diff --git a/idea/testData/quickfix/autoImports/nestedClass.before.data.Sample.kt b/idea/testData/quickfix/autoImports/nestedClass.before.data.Sample.kt
new file mode 100644
index 00000000000..73cb69ff065
--- /dev/null
+++ b/idea/testData/quickfix/autoImports/nestedClass.before.data.Sample.kt
@@ -0,0 +1,5 @@
+package test
+
+class Test {
+ class Nested
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
index 9b9cf6e4b01..93e7e8d6658 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
@@ -134,6 +134,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
+ @TestMetadata("nestedClass.before.Main.kt")
+ public void testNestedClass() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/nestedClass.before.Main.kt");
+ doTestWithExtraFile(fileName);
+ }
+
@TestMetadata("noImportForFunInQualifiedNotFirst.before.Main.kt")
public void testNoImportForFunInQualifiedNotFirst() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noImportForFunInQualifiedNotFirst.before.Main.kt");