diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt index f5d3f3f5b7a..3abe0903209 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getJavaFieldDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.stubindex.* @@ -145,7 +146,18 @@ public class KotlinIndicesHelper( } .flatMap { index.get(it, project, scope).asSequence() } - return findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType) + val suitableExtensions = findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType) + + val additionalDescriptors = ArrayList(0) + + for (extension in KotlinIndicesHelperExtension.getInstances(project)) { + extension.appendExtensionCallables(additionalDescriptors, moduleDescriptor, receiverTypes, nameFilter) + } + + return if (additionalDescriptors.isNotEmpty()) + suitableExtensions + additionalDescriptors + else + suitableExtensions } private fun MutableCollection.addTypeNames(type: KotlinType) { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/extension/KotlinIndicesHelperExtension.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/extension/KotlinIndicesHelperExtension.kt new file mode 100644 index 00000000000..68c0b601a6f --- /dev/null +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/extension/KotlinIndicesHelperExtension.kt @@ -0,0 +1,33 @@ +/* + * 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.core.extension + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor +import org.jetbrains.kotlin.types.KotlinType + +interface KotlinIndicesHelperExtension { + companion object : ProjectExtensionDescriptor( + "org.jetbrains.kotlin.kotlinIndicesHelperExtension", KotlinIndicesHelperExtension::class.java) + + fun appendExtensionCallables( + consumer: MutableList, + moduleDescriptor: ModuleDescriptor, + receiverTypes: Collection, + nameFilter: (String) -> Boolean) +} \ No newline at end of file diff --git a/idea/src/META-INF/android.xml b/idea/src/META-INF/android.xml index 58747fe0e1c..10d4d23f46c 100644 --- a/idea/src/META-INF/android.xml +++ b/idea/src/META-INF/android.xml @@ -17,5 +17,6 @@ + diff --git a/idea/src/META-INF/extensions/common.xml b/idea/src/META-INF/extensions/common.xml index eb53cd13743..09d6e7ac8b9 100644 --- a/idea/src/META-INF/extensions/common.xml +++ b/idea/src/META-INF/extensions/common.xml @@ -20,6 +20,9 @@ + diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt index acda244491a..ee96b5bb833 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.android.synthetic.descriptors -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.FqName @@ -33,6 +30,7 @@ class PredefinedPackageFragmentDescriptor( fqName: String, module: ModuleDescriptor, private val storageManager: StorageManager, + val subpackages: List = emptyList(), private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection = { emptyList() } ) : PackageFragmentDescriptorImpl(module, FqName(fqName)) { private val calculatedDescriptors = storageManager.createLazyValue { diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt index e55edbdfddf..749158db375 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.storage.StorageManager +import java.util.* abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension { protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? @@ -49,44 +50,51 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider val lazyContext = LazySyntheticElementResolveContext(module, storageManager) - val packageDescriptors = arrayListOf() + val allPackageDescriptors = arrayListOf() + val packagesToLookupInCompletion = arrayListOf() // Packages with synthetic properties for (variantData in moduleData) { for ((layoutName, layouts) in variantData) { - fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false): PackageFragmentDescriptor { + fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false) { val resources = layoutXmlFileManager.extractResources(layouts, module) val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources) - return AndroidSyntheticPackageFragmentDescriptor(module, FqName(fqName), packageData, lazyContext, storageManager) + val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor( + module, FqName(fqName), packageData, lazyContext, storageManager) + packagesToLookupInCompletion += packageDescriptor + allPackageDescriptors += packageDescriptor } val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName - packageDescriptors += createPackageFragment(packageFqName, false) - packageDescriptors += createPackageFragment(packageFqName + ".view", true) + createPackageFragment(packageFqName, false) + createPackageFragment(packageFqName + ".view", true) if (variantData.variant.isMainVariant && !moduleData.any { it.variant.name == layoutName }) { val deprecatedPackageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + layoutName - packageDescriptors += createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true) - packageDescriptors += createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true) + createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true) + createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true) } } } // Empty middle packages AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s -> - packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) + allPackageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) } + for (variantData in moduleData) { val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name - packageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager) + allPackageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager) } // Package with clearFindViewByIdCache() AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s -> - packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) { descriptor -> + val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor -> lazyContext().getWidgetReceivers(false).map { genClearCacheFunction(descriptor, it) } } + packagesToLookupInCompletion += packageDescriptor + allPackageDescriptors += packageDescriptor } val provider = AndroidSyntheticPackageFragmentProvider(allPackageDescriptors) @@ -95,7 +103,9 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider } } -class AndroidSyntheticPackageFragmentProvider(val packageFragments: Collection) : PackageFragmentProvider { +class AndroidSyntheticPackageFragmentProvider( + val packageFragments: Collection +) : PackageFragmentProvider { override fun getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName } override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) = diff --git a/plugins/android-idea-plugin/android-idea-plugin.iml b/plugins/android-idea-plugin/android-idea-plugin.iml index 424d6a7ba94..770742cf8da 100644 --- a/plugins/android-idea-plugin/android-idea-plugin.iml +++ b/plugins/android-idea-plugin/android-idea-plugin.iml @@ -12,6 +12,7 @@ + diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidIndicesHelperExtension.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidIndicesHelperExtension.kt new file mode 100644 index 00000000000..cd99822419f --- /dev/null +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidIndicesHelperExtension.kt @@ -0,0 +1,59 @@ +/* + * 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.android.synthetic.idea + +import org.jetbrains.kotlin.android.synthetic.AndroidConst +import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor +import org.jetbrains.kotlin.android.synthetic.descriptors.PredefinedPackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf + +class AndroidIndicesHelperExtension : KotlinIndicesHelperExtension { + + override fun appendExtensionCallables( + consumer: MutableList, + moduleDescriptor: ModuleDescriptor, + receiverTypes: Collection, + nameFilter: (String) -> Boolean + ) { + for (packageFragment in moduleDescriptor.getPackage(FqName(AndroidConst.SYNTHETIC_PACKAGE)).fragments) { + if (packageFragment !is PredefinedPackageFragmentDescriptor) continue + + fun handleScope(scope: MemberScope) { + val descriptors = scope.getContributedDescriptors(DescriptorKindFilter.CALLABLES) { nameFilter(it.asString()) } + for (descriptor in descriptors) { + val receiverType = (descriptor as CallableDescriptor).extensionReceiverParameter?.type ?: continue + if (receiverTypes.any { it.isSubtypeOf(receiverType) }) { + consumer += descriptor + } + } + } + + handleScope(packageFragment.getMemberScope()) + for (fragment in packageFragment.subpackages) { + if (fragment is AndroidSyntheticPackageFragmentDescriptor && fragment.packageData.isDeprecated) continue + handleScope(fragment.getMemberScope()) + } + } + } +} \ No newline at end of file diff --git a/plugins/android-idea-plugin/testData/android/completion/withoutImport/res/layout/layout.xml b/plugins/android-idea-plugin/testData/android/completion/withoutImport/res/layout/layout.xml new file mode 100644 index 00000000000..c19b620e5cc --- /dev/null +++ b/plugins/android-idea-plugin/testData/android/completion/withoutImport/res/layout/layout.xml @@ -0,0 +1,10 @@ + + +