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.getJavaMethodDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor 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.imports.importableFqName
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.stubindex.*
@@ -145,7 +146,18 @@ public class KotlinIndicesHelper(
} }
.flatMap { index.get(it, project, scope).asSequence() } .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) { 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"/> <classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension"/>
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/> <packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/> <simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>
+3
View File
@@ -20,6 +20,9 @@
<extensionPoint name="simpleNameReferenceExtension" <extensionPoint name="simpleNameReferenceExtension"
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension" interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
area="IDEA_PROJECT"/> area="IDEA_PROJECT"/>
<extensionPoint name="kotlinIndicesHelperExtension"
interface="org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension"
area="IDEA_PROJECT"/>
<extensionPoint name="classBuilderFactoryInterceptorExtension" <extensionPoint name="classBuilderFactoryInterceptorExtension"
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension" interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
area="IDEA_PROJECT"/> area="IDEA_PROJECT"/>
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.descriptors package org.jetbrains.kotlin.android.synthetic.descriptors
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -33,6 +30,7 @@ class PredefinedPackageFragmentDescriptor(
fqName: String, fqName: String,
module: ModuleDescriptor, module: ModuleDescriptor,
private val storageManager: StorageManager, private val storageManager: StorageManager,
val subpackages: List<PackageFragmentDescriptor> = emptyList(),
private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection<DeclarationDescriptor> = { emptyList() } private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection<DeclarationDescriptor> = { emptyList() }
) : PackageFragmentDescriptorImpl(module, FqName(fqName)) { ) : PackageFragmentDescriptorImpl(module, FqName(fqName)) {
private val calculatedDescriptors = storageManager.createLazyValue { 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.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import java.util.*
abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension { abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension {
protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager?
@@ -49,44 +50,51 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
val lazyContext = LazySyntheticElementResolveContext(module, storageManager) val lazyContext = LazySyntheticElementResolveContext(module, storageManager)
val packageDescriptors = arrayListOf<PackageFragmentDescriptor>() val allPackageDescriptors = arrayListOf<PackageFragmentDescriptor>()
val packagesToLookupInCompletion = arrayListOf<PackageFragmentDescriptor>()
// Packages with synthetic properties // Packages with synthetic properties
for (variantData in moduleData) { for (variantData in moduleData) {
for ((layoutName, layouts) in variantData) { 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 resources = layoutXmlFileManager.extractResources(layouts, module)
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources) 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 val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName
packageDescriptors += createPackageFragment(packageFqName, false) createPackageFragment(packageFqName, false)
packageDescriptors += createPackageFragment(packageFqName + ".view", true) createPackageFragment(packageFqName + ".view", true)
if (variantData.variant.isMainVariant && !moduleData.any { it.variant.name == layoutName }) { if (variantData.variant.isMainVariant && !moduleData.any { it.variant.name == layoutName }) {
val deprecatedPackageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + layoutName val deprecatedPackageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + layoutName
packageDescriptors += createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true) createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true)
packageDescriptors += createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true) createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true)
} }
} }
} }
// Empty middle packages // Empty middle packages
AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s -> AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s ->
packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) allPackageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager)
} }
for (variantData in moduleData) { for (variantData in moduleData) {
val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name
packageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager) allPackageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
} }
// Package with clearFindViewByIdCache() // Package with clearFindViewByIdCache()
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s -> 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) } lazyContext().getWidgetReceivers(false).map { genClearCacheFunction(descriptor, it) }
} }
packagesToLookupInCompletion += packageDescriptor
allPackageDescriptors += packageDescriptor
} }
val provider = AndroidSyntheticPackageFragmentProvider(allPackageDescriptors) 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 getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName }
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) = 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" exported="" scope="PROVIDED" name="android-plugin" level="project" />
<orderEntry type="library" scope="PROVIDED" name="idea-full" 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" 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="compiler-tests" scope="TEST" />
<orderEntry type="module" module-name="cli" scope="TEST" /> <orderEntry type="module" module-name="cli" scope="TEST" />
<orderEntry type="module" module-name="light-classes" /> <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/"); String fileName = KotlinTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/completion/propertiesSimpleView/");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("withoutImport")
public void testWithoutImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/completion/withoutImport/");
doTest(fileName);
}
} }