Autoimport items from kotlinx.android.synthetic on completion

This commit is contained in:
Yan Zhulanow
2015-11-12 16:49:50 +03:00
parent 0dada9d13a
commit 3f97b384ef
11 changed files with 158 additions and 16 deletions
@@ -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<CallableDescriptor>(0)
for (extension in KotlinIndicesHelperExtension.getInstances(project)) {
extension.appendExtensionCallables(additionalDescriptors, moduleDescriptor, receiverTypes, nameFilter)
}
return if (additionalDescriptors.isNotEmpty())
suitableExtensions + additionalDescriptors
else
suitableExtensions
}
private fun MutableCollection<String>.addTypeNames(type: KotlinType) {
@@ -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<KotlinIndicesHelperExtension>(
"org.jetbrains.kotlin.kotlinIndicesHelperExtension", KotlinIndicesHelperExtension::class.java)
fun appendExtensionCallables(
consumer: MutableList<in CallableDescriptor>,
moduleDescriptor: ModuleDescriptor,
receiverTypes: Collection<KotlinType>,
nameFilter: (String) -> Boolean)
}
+1
View File
@@ -17,5 +17,6 @@
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension"/>
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
</extensions>
</idea-plugin>
+3
View File
@@ -20,6 +20,9 @@
<extensionPoint name="simpleNameReferenceExtension"
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
area="IDEA_PROJECT"/>
<extensionPoint name="kotlinIndicesHelperExtension"
interface="org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension"
area="IDEA_PROJECT"/>
<extensionPoint name="classBuilderFactoryInterceptorExtension"
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
area="IDEA_PROJECT"/>
@@ -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<PackageFragmentDescriptor> = emptyList(),
private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection<DeclarationDescriptor> = { emptyList() }
) : PackageFragmentDescriptorImpl(module, FqName(fqName)) {
private val calculatedDescriptors = storageManager.createLazyValue {
@@ -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<PackageFragmentDescriptor>()
val allPackageDescriptors = arrayListOf<PackageFragmentDescriptor>()
val packagesToLookupInCompletion = arrayListOf<PackageFragmentDescriptor>()
// 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<PackageFragmentDescriptor>) : PackageFragmentProvider {
class AndroidSyntheticPackageFragmentProvider(
val packageFragments: Collection<PackageFragmentDescriptor>
) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName }
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) =
@@ -12,6 +12,7 @@
<orderEntry type="library" exported="" scope="PROVIDED" name="android-plugin" level="project" />
<orderEntry type="library" scope="PROVIDED" name="idea-full" level="project" />
<orderEntry type="module" module-name="idea" scope="PROVIDED" />
<orderEntry type="module" module-name="idea-core" scope="PROVIDED" />
<orderEntry type="module" module-name="compiler-tests" scope="TEST" />
<orderEntry type="module" module-name="cli" scope="TEST" />
<orderEntry type="module" module-name="light-classes" />
@@ -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<in CallableDescriptor>,
moduleDescriptor: ModuleDescriptor,
receiverTypes: Collection<KotlinType>,
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())
}
}
}
}
@@ -0,0 +1,10 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
@@ -0,0 +1,9 @@
package com.myapp
import android.app.Activity
class MyActivity: Activity() {
val button = this.login<caret>
}
// EXIST: login
@@ -88,4 +88,10 @@ public class AndroidCompletionTestGenerated extends AbstractAndroidCompletionTes
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/completion/propertiesSimpleView/");
doTest(fileName);
}
@TestMetadata("withoutImport")
public void testWithoutImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/completion/withoutImport/");
doTest(fileName);
}
}