Android Extensions: Make AndroidPackageFragmentProviderExtension lazy

This commit is contained in:
Yan Zhulanow
2018-11-01 16:56:03 +09:00
parent 6f8d44750a
commit 4f3f813b32
5 changed files with 72 additions and 43 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.synthetic.descriptors
import com.intellij.openapi.util.ClearableLazyValue
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -30,16 +31,27 @@ import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.utils.Printer
class PredefinedPackageFragmentDescriptor(
fqName: String,
fqName: FqName,
module: ModuleDescriptor,
storageManager: StorageManager,
val subpackages: List<PackageFragmentDescriptor> = emptyList(),
val lazySubpackages: List<LazyAndroidExtensionsPackageFragmentDescriptor> = emptyList(),
private val functions: (PredefinedPackageFragmentDescriptor) -> Collection<SimpleFunctionDescriptor> = { emptyList() }
) : PackageFragmentDescriptorImpl(module, FqName(fqName)) {
) : PackageFragmentDescriptorImpl(module, fqName) {
class LazyAndroidExtensionsPackageFragmentDescriptor(
val descriptor: ClearableLazyValue<PackageFragmentDescriptor>,
val isDeprecated: Boolean
)
private val calculatedFunctions = storageManager.createLazyValue {
functions(this)
}
// Left for compatibility with Android Studio
@Deprecated("Use lazySubpackages instead.", ReplaceWith("lazySubpackages"))
@Suppress("unused")
val subpackages: List<PackageFragmentDescriptor>
get() = lazySubpackages.map { it.descriptor() }
private val scope = PredefinedScope()
override fun getMemberScope() = scope
@@ -17,12 +17,14 @@
package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ClearableLazyValue
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageData
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
import org.jetbrains.kotlin.android.synthetic.descriptors.LazySyntheticElementResolveContext
import org.jetbrains.kotlin.android.synthetic.descriptors.PredefinedPackageFragmentDescriptor
import org.jetbrains.kotlin.android.synthetic.descriptors.PredefinedPackageFragmentDescriptor.LazyAndroidExtensionsPackageFragmentDescriptor
import org.jetbrains.kotlin.android.synthetic.forEachUntilLast
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
@@ -55,22 +57,27 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
val lazyContext = LazySyntheticElementResolveContext(module, storageManager)
val allPackageDescriptors = arrayListOf<PackageFragmentDescriptor>()
val packagesToLookupInCompletion = arrayListOf<PackageFragmentDescriptor>()
val packages = mutableMapOf<FqName, ClearableLazyValue<PackageFragmentDescriptor>>()
val packagesToLookupInCompletion = mutableListOf<LazyAndroidExtensionsPackageFragmentDescriptor>()
// Packages with synthetic properties
for (variantData in moduleData.variants) {
for ((layoutName, layouts) in variantData.layouts) {
fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false) {
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated) {
layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module)
}
val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor(
module, FqName(fqName), packageData, lazyContext, storageManager, isExperimental,
fun createPackageFragment(fqNameString: String, forView: Boolean, isDeprecated: Boolean = false) {
val fqName = FqName(fqNameString)
val lazyPackageDescriptor = ClearableLazyValue.create<PackageFragmentDescriptor> {
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated) {
layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module)
}
AndroidSyntheticPackageFragmentDescriptor(
module, fqName, packageData, lazyContext, storageManager, isExperimental,
lookupTracker, layoutName
)
packagesToLookupInCompletion += packageDescriptor
allPackageDescriptors += packageDescriptor
)
}
packages[fqName] = lazyPackageDescriptor
packagesToLookupInCompletion += LazyAndroidExtensionsPackageFragmentDescriptor(lazyPackageDescriptor, isDeprecated)
}
val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName
@@ -81,40 +88,50 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
}
// Empty middle packages
AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s ->
allPackageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager)
AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { fqNameString ->
val fqName = FqName(fqNameString)
packages[fqName] = ClearableLazyValue.create<PackageFragmentDescriptor> {
PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
}
}
for (variantData in moduleData.variants) {
val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name
allPackageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
val fqName = FqName(AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name)
packages[fqName] = ClearableLazyValue.create<PackageFragmentDescriptor> {
PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
}
}
// Package with clearFindViewByIdCache()
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s ->
val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor ->
val widgetReceivers = lazyContext().getWidgetReceivers(forView = false, isExperimental = isExperimental) +
lazyContext().getWidgetReceivers(forView = true, isExperimental = isExperimental)
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { fqNameString ->
val fqName = FqName(fqNameString)
val lazyPackageDescriptor = ClearableLazyValue.create<PackageFragmentDescriptor> {
PredefinedPackageFragmentDescriptor(fqName, module, storageManager, packagesToLookupInCompletion) { descriptor ->
val widgetReceivers = lazyContext().getWidgetReceivers(forView = false, isExperimental = isExperimental) +
lazyContext().getWidgetReceivers(forView = true, isExperimental = isExperimental)
widgetReceivers
widgetReceivers
.filter { it.mayHaveCache }
.map { genClearCacheFunction(descriptor, it.type) }
}
}
packagesToLookupInCompletion += packageDescriptor
allPackageDescriptors += packageDescriptor
packages[fqName] = lazyPackageDescriptor
packagesToLookupInCompletion += LazyAndroidExtensionsPackageFragmentDescriptor(lazyPackageDescriptor, false)
}
return AndroidSyntheticPackageFragmentProvider(allPackageDescriptors)
return AndroidSyntheticPackageFragmentProvider(packages)
}
}
class AndroidSyntheticPackageFragmentProvider(val packageFragments: Collection<PackageFragmentDescriptor>) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName }
class AndroidSyntheticPackageFragmentProvider(
val packages: Map<FqName, ClearableLazyValue<PackageFragmentDescriptor>>
) : PackageFragmentProvider {
override fun getPackageFragments(fqName: FqName) = listOfNotNull(packages[fqName]?.value)
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): List<FqName> {
return packageFragments.asSequence()
.map { it.fqName }
.filter { !it.isRoot && it.parent() == fqName }
.toList()
return packages.asSequence()
.filter { (k, _) -> !k.isRoot && k.parent() == fqName }
.mapTo(mutableListOf()) { it.key }
}
}
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo
@@ -47,12 +47,15 @@ abstract class AbstractAndroidSyntheticPropertyDescriptorTest : KtUsefulTestCase
) as AndroidSyntheticPackageFragmentProvider
val renderer = DescriptorRenderer.COMPACT_WITH_MODIFIERS
val expected = fragmentProvider.packageFragments.sortedBy { it.fqName.asString() }.map {
val descriptors = it.getMemberScope().getContributedDescriptors()
val expected = fragmentProvider.packages.values
.map { it() }
.sortedBy { it.fqName.asString() }
.joinToString(separator = "\n\n\n") {
val descriptors = it.getMemberScope().getContributedDescriptors()
.sortedWith(MemberComparator.INSTANCE)
.map { " " + renderer.render(it) }.joinToString("\n")
it.fqName.asString() + (if (descriptors.isNotEmpty()) "\n\n" + descriptors else "")
}.joinToString("\n\n\n")
it.fqName.asString() + (if (descriptors.isNotEmpty()) "\n\n" + descriptors else "")
}
KotlinTestUtils.assertEqualsToFile(File(path, "result.txt"), expected)
}
@@ -17,7 +17,6 @@
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
@@ -29,7 +28,6 @@ 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,
@@ -50,9 +48,9 @@ class AndroidIndicesHelperExtension : KotlinIndicesHelperExtension {
}
handleScope(packageFragment.getMemberScope())
for (fragment in packageFragment.subpackages) {
if (fragment is AndroidSyntheticPackageFragmentDescriptor && fragment.packageData.isDeprecated) continue
handleScope(fragment.getMemberScope())
for (fragment in packageFragment.lazySubpackages) {
if (fragment.isDeprecated) continue
handleScope(fragment.descriptor().getMemberScope())
}
}
}