Remove code that was migrated to android studio repo.

This commit is contained in:
Mads Ager
2022-01-25 13:45:32 +01:00
committed by Alexander Udalov
parent 6f17a8713c
commit 5147a9e42d
217 changed files with 0 additions and 6179 deletions
@@ -1,90 +0,0 @@
description = "Kotlin Android Extensions IDEA"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":plugins:android-extensions-compiler"))
testRuntime(intellijDep())
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:light-classes"))
compileOnly(project(":idea:idea-core"))
compileOnly(project(":idea"))
compileOnly(project(":idea:idea-jvm"))
compileOnly(project(":idea:idea-gradle"))
compileOnly(project(":kotlin-android-extensions-runtime"))
compileOnly(intellijPluginDep("android"))
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijPluginDep("Groovy"))
compileOnly(intellijDep())
testApi(project(":compiler:cli"))
testApi(project(":compiler:frontend.java"))
testApi(projectTests(":idea:idea-test-framework")) { isTransitive = false }
testApi(project(":plugins:kapt3-idea"))
testApi(projectTests(":compiler:tests-common"))
testApi(projectTests(":idea"))
testApi(projectTests(":idea:idea-android"))
testApi(project(":kotlin-test:kotlin-test-jvm"))
testApi(commonDependency("junit:junit"))
testApi(project(":idea:idea-native")) { isTransitive = false }
testApi(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":native:frontend.native"))
testRuntime(project(":kotlin-reflect"))
testApi(intellijPluginDep("android"))
testApi(intellijPluginDep("Groovy"))
testApi(intellijDep())
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":kotlin-scripting-idea"))
testRuntime(project(":kotlinx-serialization-ide-plugin"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":plugins:parcelize:parcelize-ide"))
testRuntime(project(":plugins:lombok:lombok-ide-plugin"))
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("IntelliLang"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("java-i18n"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("java-decompiler"))
Ide.IJ {
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("repository-search"))
}
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
Ide.AS {
testRuntime(intellijPluginDep("android-layoutlib"))
testRuntime(intellijPluginDep("platform-images"))
}
}
sourceSets {
"main" { }
"test" { }
}
testsJar {}
projectTest(parallel = true) {
dependsOn(":dist")
workingDir = rootDir
useAndroidSdk()
useAndroidJar()
}
runtimeJar()
sourcesJar()
javadocJar()
@@ -1 +0,0 @@
org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsModelBuilderService
@@ -1,43 +0,0 @@
/*
* Copyright 2010-2017 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
import com.intellij.psi.xml.XmlAttributeValue
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.completion.CompletionInformationProvider
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
class AndroidExtensionsCompletionInformationProvider : CompletionInformationProvider {
override fun getContainerAndReceiverInformation(descriptor: DeclarationDescriptor): String? {
if (descriptor !is AndroidSyntheticProperty) {
return null
}
val propertyDescriptor = (descriptor as? PropertyDescriptor) ?: return null
val attributeValue = (propertyDescriptor.source as? PsiSourceElement)?.psi as? XmlAttributeValue ?: return null
val extensionReceiverType = propertyDescriptor.original.extensionReceiverParameter?.type
return buildString {
append(" from ${attributeValue.containingFile.name}")
extensionReceiverType?.let { append(" for " + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it)) }
append(" (Android Extensions)")
}
}
}
@@ -1,61 +0,0 @@
/*
* Copyright 2010-2017 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
import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.util.Processor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.resolve.source.getPsi
class AndroidExtensionsReferenceSearchExecutor : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>(true) {
override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor<in PsiReference>) {
val elementToSearch = queryParameters.elementToSearch as? XmlAttributeValue ?: return
val scopeElements = (queryParameters.effectiveSearchScope as? LocalSearchScope)?.scope ?: return
val referenceName = elementToSearch.value?.substringAfterLast("/") ?: return
scopeElements.filterIsInstance<KtElement>().forEach {
it.accept(object : KtTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
if (expression.text == referenceName && expression.isReferenceTo(elementToSearch)) {
expression.references.firstOrNull { it is KtSimpleNameReference }?.let {
consumer.process(it)
}
}
super.visitReferenceExpression(expression)
}
})
}
}
private fun KtReferenceExpression.isReferenceTo(element: PsiElement): Boolean =
getTargetPropertyDescriptor()?.source?.getPsi() == element
private fun KtReferenceExpression.getTargetPropertyDescriptor(): PropertyDescriptor? =
resolveToCall()?.resultingDescriptor as? PropertyDescriptor
}
@@ -1,64 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.android.model
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import java.io.File
val Module.isAndroidModule
get() = AndroidModuleInfoProvider.getInstance(this)?.isAndroidModule() ?: false
interface AndroidModuleInfoProvider {
companion object {
val EP_NAME = ExtensionPointName.create<AndroidModuleInfoProvider>("org.jetbrains.kotlin.android.model.androidModuleInfoProvider")
fun getInstance(module: Module): AndroidModuleInfoProvider? {
val extensionArea = Extensions.getArea(module)
if (!extensionArea.hasExtensionPoint(EP_NAME.name)) {
return null
}
return extensionArea.getExtensionPoint(EP_NAME).extension
}
fun getInstance(element: PsiElement): AndroidModuleInfoProvider? {
val module = ApplicationManager.getApplication().runReadAction<Module> {
ModuleUtilCore.findModuleForPsiElement(element)
}
return getInstance(module)
}
}
val module: Module
fun isAndroidModule(): Boolean
fun isGradleModule(): Boolean
fun getApplicationPackage(): String?
// For old Android Extensions
fun getMainSourceProvider(): SourceProviderMirror?
fun getFlavorSourceProviders(): List<SourceProviderMirror>
fun getAllResourceDirectories(): List<VirtualFile>
// For experimental Android Extensions
fun getApplicationResourceDirectories(createIfNecessary: Boolean): Collection<VirtualFile>
fun getAllSourceProviders(): List<SourceProviderMirror>
fun getActiveSourceProviders(): List<SourceProviderMirror>
interface SourceProviderMirror {
val name: String
val resDirectories: Collection<File>
}
}
@@ -1,78 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.android.model.impl
import com.android.builder.model.SourceProvider
import com.android.tools.idea.gradle.project.GradleProjectInfo
import com.android.tools.idea.gradle.project.model.AndroidModuleModel
import com.android.tools.idea.res.ResourceRepositoryManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider
import java.io.File
class AndroidModuleInfoProviderImpl(override val module: Module) : AndroidModuleInfoProvider {
private val androidFacet: AndroidFacet?
get() = AndroidFacet.getInstance(module)
private val androidModuleModel: AndroidModuleModel?
get() = AndroidModuleModel.get(module)
override fun isAndroidModule() = androidFacet != null
override fun isGradleModule() = GradleProjectInfo.getInstance(module.project).isBuildWithGradle
override fun getAllResourceDirectories(): List<VirtualFile> {
return androidFacet?.allResourceDirectories ?: emptyList()
}
override fun getApplicationPackage() = androidFacet?.manifest?.`package`?.toString()
override fun getMainSourceProvider(): AndroidModuleInfoProvider.SourceProviderMirror? {
return androidFacet?.mainSourceProvider?.let(::SourceProviderMirrorImpl)
}
override fun getApplicationResourceDirectories(createIfNecessary: Boolean): Collection<VirtualFile> {
return ResourceRepositoryManager.getOrCreateInstance(module)?.getAppResources(createIfNecessary)?.resourceDirs ?: emptyList()
}
override fun getAllSourceProviders(): List<AndroidModuleInfoProvider.SourceProviderMirror> {
val androidModuleModel = this.androidModuleModel ?: return emptyList()
return androidModuleModel.allSourceProviders.map(::SourceProviderMirrorImpl)
}
override fun getActiveSourceProviders(): List<AndroidModuleInfoProvider.SourceProviderMirror> {
val androidModuleModel = this.androidModuleModel ?: return emptyList()
return androidModuleModel.activeSourceProviders.map(::SourceProviderMirrorImpl)
}
override fun getFlavorSourceProviders(): List<AndroidModuleInfoProvider.SourceProviderMirror> {
val androidModuleModel = this.androidModuleModel ?: return emptyList()
val getFlavorSourceProvidersMethod = try {
AndroidFacet::class.java.getMethod("getFlavorSourceProviders")
} catch (e: NoSuchMethodException) {
null
}
return if (getFlavorSourceProvidersMethod != null) {
@Suppress("UNCHECKED_CAST")
val sourceProviders = getFlavorSourceProvidersMethod.invoke(androidFacet) as? List<SourceProvider>
sourceProviders?.map(::SourceProviderMirrorImpl) ?: emptyList()
} else {
androidModuleModel.flavorSourceProviders.map(::SourceProviderMirrorImpl)
}
}
private class SourceProviderMirrorImpl(val sourceProvider: SourceProvider) :
AndroidModuleInfoProvider.SourceProviderMirror {
override val name: String
get() = sourceProvider.name
override val resDirectories: Collection<File>
get() = sourceProvider.resDirectories
}
}
@@ -1,17 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.android.parcel
import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsExperimental
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.psi.KtElement
class IDEParcelableCodegenExtension : ParcelableCodegenExtension() {
override fun isExperimental(element: KtElement): Boolean {
val moduleInfo = element.getModuleInfo()
return moduleInfo.androidExtensionsIsExperimental
}
}
@@ -1,28 +0,0 @@
/*
* Copyright 2010-2017 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.parcel
import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsExperimental
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.psi.KtElement
class IDEParcelableResolveExtension : ParcelableResolveExtension() {
override fun isExperimental(element: KtElement): Boolean {
val moduleInfo = element.getModuleInfo()
return moduleInfo.androidExtensionsIsExperimental
}
}
@@ -1,101 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.android.parcel
import com.intellij.psi.impl.light.LightFieldBuilder
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase
import org.jetbrains.kotlin.asJava.UltraLightClassModifierExtension
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.asJava.classes.createGeneratedMethodFromDescriptor
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.util.isAnnotated
import org.jetbrains.kotlin.util.isOrdinaryClass
class ParcelableUltraLightClassModifierExtension : ParcelableExtensionBase, UltraLightClassModifierExtension {
private fun tryGetParcelableClass(
declaration: KtDeclaration,
descriptor: Lazy<DeclarationDescriptor?>
): ClassDescriptor? {
if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return null
val descriptorValue = descriptor.value ?: return null
val parcelableClass = (descriptorValue as? ClassDescriptor)
?: descriptorValue.containingDeclaration as? ClassDescriptor
?: return null
if (!parcelableClass.isParcelableClassDescriptor) return null
return parcelableClass
}
override fun interceptFieldsBuilding(
declaration: KtDeclaration,
descriptor: Lazy<DeclarationDescriptor?>,
containingDeclaration: KtUltraLightClass,
fieldsList: MutableList<KtLightField>
) {
val parcelableClass = tryGetParcelableClass(
declaration = declaration,
descriptor = descriptor
) ?: return
if (parcelableClass.hasCreatorField()) return
val fieldWrapper = KtLightFieldImpl.KtLightFieldForSourceDeclaration(
origin = null,
computeDelegate = {
LightFieldBuilder("CREATOR", "android.os.Parcelable.Creator", containingDeclaration).also {
it.setModifiers("public", "static", "final")
}
},
containingClass = containingDeclaration,
dummyDelegate = null
)
fieldsList.add(fieldWrapper)
}
override fun interceptMethodsBuilding(
declaration: KtDeclaration,
descriptor: Lazy<DeclarationDescriptor?>,
containingDeclaration: KtUltraLightClass,
methodsList: MutableList<KtLightMethod>
) {
val parcelableClass = tryGetParcelableClass(
declaration = declaration,
descriptor = descriptor
) ?: return
with(parcelableClass) {
if (hasSyntheticDescribeContents()) {
findFunction(ParcelableSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)?.let {
methodsList.add(
containingDeclaration.createGeneratedMethodFromDescriptor(it)
)
}
}
if (hasSyntheticWriteToParcel()) {
findFunction(ParcelableSyntheticComponent.ComponentKind.WRITE_TO_PARCEL)?.let {
methodsList.add(
containingDeclaration.createGeneratedMethodFromDescriptor(it)
)
}
}
}
}
}
@@ -1,53 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
abstract class AbstractParcelableQuickFix<T: KtElement>(element: T) : KotlinQuickFixAction<T>(element) {
protected companion object {
fun <T : KtElement> T.shortenReferences() = ShortenReferences.DEFAULT.process(this)
}
override fun getFamilyName() = text
abstract fun invoke(ktPsiFactory: KtPsiFactory, element: T)
final override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val clazz = element ?: return
val ktPsiFactory = KtPsiFactory(project, markGenerated = true)
invoke(ktPsiFactory, clazz)
}
abstract class AbstractFactory(private val f: Diagnostic.() -> IntentionAction?) : KotlinSingleIntentionActionFactory() {
companion object {
inline fun <reified T: KtElement> Diagnostic.findElement() = psiElement.getNonStrictParentOfType<T>()
}
override fun createAction(diagnostic: Diagnostic) = f(diagnostic)
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import kotlinx.android.parcel.Parcelize
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.util.addAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtPsiFactory
class AnnotateWithParcelizeQuickFix(clazz: KtClassOrObject) : AbstractParcelableQuickFix<KtClassOrObject>(clazz) {
object Factory : AbstractFactory({
val diagnostic = Errors.PLUGIN_ERROR.cast(this).a
val targetClass = ErrorsAndroid.CLASS_SHOULD_BE_PARCELIZE.cast(diagnostic.diagnostic).a
AnnotateWithParcelizeQuickFix(targetClass)
})
override fun getText() = "Annotate containing class with ''@Parcelize''"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtClassOrObject) {
element.addAnnotation(FqName(Parcelize::class.java.name))
}
}
@@ -1,287 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import com.intellij.openapi.diagnostic.Logger
import kotlinx.android.parcel.Parceler
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCEL_CLASS_FQNAME
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis
import org.jetbrains.kotlin.idea.util.findAnnotation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.TypeUtils
class ParcelMigrateToParcelizeQuickFix(function: KtClass) : AbstractParcelableQuickFix<KtClass>(function) {
companion object {
private val PARCELER_FQNAME = FqName(Parceler::class.java.name)
private val PARCELER_WRITE_FUNCTION_NAME = Name.identifier("write")
private val PARCELER_CREATE_FUNCTION_NAME = Name.identifier("create")
private val LOG = Logger.getInstance(ParcelMigrateToParcelizeQuickFix::class.java)
private fun KtClass.findParcelerCompanionObject(): Pair<KtObjectDeclaration, ClassDescriptor>? {
for (obj in companionObjects) {
val objDescriptor = obj.resolveToDescriptorIfAny() ?: continue
for (superClassifier in objDescriptor.getAllSuperClassifiers()) {
val superClass = superClassifier as? ClassDescriptor ?: continue
if (superClass.fqNameSafe == PARCELER_FQNAME) return Pair(obj, objDescriptor)
}
}
return null
}
private fun KtNamedFunction.doesLookLikeWriteToParcelOverride(): Boolean {
return name == "writeToParcel"
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference == null
&& valueParameters.size == 2
&& typeParameters.size == 0
&& valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString()
&& valueParameters[1].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString()
}
private fun KtNamedFunction.doesLookLikeNewArrayOverride(): Boolean {
return name == "newArray"
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference == null
&& valueParameters.size == 1
&& typeParameters.size == 0
&& valueParameters[0].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString()
}
private fun KtNamedFunction.doesLookLikeDescribeContentsOverride(): Boolean {
return name == "describeContents"
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference == null
&& valueParameters.size == 0
&& typeParameters.size == 0
&& typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString()
}
private fun KtClass.findWriteToParcelOverride() = findFunction { doesLookLikeWriteToParcelOverride() }
private fun KtClass.findDescribeContentsOverride() = findFunction { doesLookLikeDescribeContentsOverride() }
private fun KtObjectDeclaration.findNewArrayOverride() = findFunction { doesLookLikeNewArrayOverride() }
private fun KtClass.findCreatorClass(): KtClassOrObject? {
for (companion in companionObjects) {
if (companion.name == "CREATOR") {
return companion
}
val creatorProperty = companion.declarations.asSequence()
.filterIsInstance<KtProperty>()
.firstOrNull { it.name == "CREATOR" }
?: continue
creatorProperty.findAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) ?: continue
val initializer = creatorProperty.initializer ?: continue
when (initializer) {
is KtObjectLiteralExpression -> return initializer.objectDeclaration
is KtCallExpression -> {
val constructedClass = (initializer.resolveToCall()
?.resultingDescriptor as? ConstructorDescriptor)?.constructedClass
if (constructedClass != null) {
val sourceElement = constructedClass.source as? KotlinSourceElement
(sourceElement?.psi as? KtClassOrObject)?.let { return it }
}
}
}
}
return null
}
private fun KtNamedFunction.doesLookLikeCreateFromParcelOverride(): Boolean {
return name == "createFromParcel"
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference == null
&& valueParameters.size == 1
&& typeParameters.size == 0
&& valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString()
}
private fun findCreateFromParcel(creator: KtClassOrObject) = creator.findFunction { doesLookLikeCreateFromParcelOverride() }
private fun KtNamedFunction.doesLookLikeWriteImplementation(): Boolean {
val containingParcelableClassFqName = containingClassOrObject?.containingClass()?.fqName?.asString()
return name == PARCELER_WRITE_FUNCTION_NAME.asString()
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference?.getFqName() == containingParcelableClassFqName
&& valueParameters.size == 2
&& typeParameters.size == 0
&& valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString()
&& valueParameters[1].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString()
}
private fun KtNamedFunction.doesLookLikeCreateImplementation(): Boolean {
return name == PARCELER_CREATE_FUNCTION_NAME.asString()
&& hasModifier(KtTokens.OVERRIDE_KEYWORD)
&& receiverTypeReference == null
&& valueParameters.size == 1
&& typeParameters.size == 0
&& valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString()
}
private fun KtObjectDeclaration.findCreateImplementation() = findFunction { doesLookLikeCreateImplementation() }
private fun KtObjectDeclaration.findWriteImplementation() = findFunction { doesLookLikeWriteImplementation() }
private fun KtClassOrObject.findFunction(f: KtNamedFunction.() -> Boolean)
= declarations.asSequence().filterIsInstance<KtNamedFunction>().firstOrNull(f)
private fun KtTypeReference.getFqName(): String? = analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, this]
?.constructor?.declarationDescriptor?.fqNameSafe?.asString()
}
object FactoryForWrite : AbstractFactory({ findElement<KtClass>()?.let { ParcelMigrateToParcelizeQuickFix(it) } })
object FactoryForCREATOR : AbstractFactory({
findElement<KtObjectDeclaration>()?.getStrictParentOfType<KtClass>()?.let { ParcelMigrateToParcelizeQuickFix(it) }
})
override fun getText() = "Migrate to ''Parceler'' companion object"
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun invoke(ktPsiFactory: KtPsiFactory, parcelableClass: KtClass) {
val parcelerObject = parcelableClass.findParcelerCompanionObject()?.first ?: parcelableClass.getOrCreateCompanionObject()
val bindingContext = parcelerObject.analyze(BodyResolveMode.PARTIAL)
val parcelerTypeArg = parcelableClass.name ?: run {
LOG.error("Parceler class should not be an anonymous class")
return
}
val parcelerObjectDescriptor = bindingContext[BindingContext.CLASS, parcelerObject] ?: run {
LOG.error("Unable to resolve parceler object for ${parcelableClass.name ?: "<unnamed Parcelable class>"}")
return
}
if (!parcelerObjectDescriptor.getAllSuperClassifiers().any { it.fqNameSafe == PARCELER_FQNAME }) {
val entryText = PARCELER_FQNAME.asString() + "<" + parcelerTypeArg + ">"
parcelerObject.addSuperTypeListEntry(ktPsiFactory.createSuperTypeEntry(entryText)).shortenReferences()
}
val oldWriteToParcelFunction = parcelableClass.findWriteToParcelOverride()
val oldCreateFromParcelFunction = parcelableClass.findCreatorClass()?.let { findCreateFromParcel(it) }
for (superTypeEntry in parcelerObject.superTypeListEntries) {
val superClass = bindingContext[BindingContext.TYPE, superTypeEntry.typeReference]?.constructor?.declarationDescriptor ?: continue
if (superClass.getAllSuperClassifiers().any { it.fqNameSafe == ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME }) {
parcelerObject.removeSuperTypeListEntry(superTypeEntry)
}
}
if (parcelerObject.name == "CREATOR") {
parcelerObject.nameIdentifier?.delete()
}
if (oldWriteToParcelFunction != null) {
parcelerObject.findWriteImplementation()?.delete() // Remove old implementation
val newFunction = oldWriteToParcelFunction.copy() as KtFunction
oldWriteToParcelFunction.delete()
newFunction.setName(PARCELER_WRITE_FUNCTION_NAME.asString())
newFunction.setModifierList(ktPsiFactory.createModifierList(KtTokens.OVERRIDE_KEYWORD))
newFunction.setReceiverTypeReference(ktPsiFactory.createType(parcelerTypeArg))
newFunction.valueParameterList?.apply {
assert(parameters.size == 2)
val parcelParameterName = parameters[0].name ?: "parcel"
val flagsParameterName = parameters[1].name ?: "flags"
repeat(parameters.size) { removeParameter(0) }
addParameter(ktPsiFactory.createParameter("$parcelParameterName : ${ANDROID_PARCEL_CLASS_FQNAME.asString()}"))
addParameter(ktPsiFactory.createParameter("$flagsParameterName : Int"))
}
parcelerObject.addDeclaration(newFunction).valueParameterList?.shortenReferences()
} else if (parcelerObject.findWriteImplementation() == null) {
val writeFunction = "fun $parcelerTypeArg.write(parcel: ${ANDROID_PARCEL_CLASS_FQNAME.asString()}, flags: Int) = TODO()"
parcelerObject.addDeclaration(ktPsiFactory.createFunction(writeFunction)).valueParameterList?.shortenReferences()
}
if (oldCreateFromParcelFunction != null) {
parcelerObject.findCreateImplementation()?.delete() // Remove old implementation
val newFunction = oldCreateFromParcelFunction.copy() as KtFunction
if (oldCreateFromParcelFunction.containingClassOrObject == parcelerObject) {
oldCreateFromParcelFunction.delete()
}
newFunction.setName(PARCELER_CREATE_FUNCTION_NAME.asString())
newFunction.setModifierList(ktPsiFactory.createModifierList(KtTokens.OVERRIDE_KEYWORD))
newFunction.setReceiverTypeReference(null)
newFunction.valueParameterList?.apply {
assert(parameters.size == 1)
val parcelParameterName = parameters[0].name ?: "parcel"
removeParameter(0)
addParameter(ktPsiFactory.createParameter("$parcelParameterName : ${ANDROID_PARCEL_CLASS_FQNAME.asString()}"))
}
parcelerObject.addDeclaration(newFunction).valueParameterList?.shortenReferences()
} else if (parcelerObject.findCreateImplementation() == null) {
val createFunction = "override fun create(parcel: ${ANDROID_PARCEL_CLASS_FQNAME.asString()}): $parcelerTypeArg = TODO()"
parcelerObject.addDeclaration(ktPsiFactory.createFunction(createFunction)).valueParameterList?.shortenReferences()
}
// Always use the default newArray() implementation
parcelerObject.findNewArrayOverride()?.delete()
parcelableClass.findDescribeContentsOverride()?.let { describeContentsFunction ->
val returnExpr = describeContentsFunction.bodyExpression?.unwrapBlockOrParenthesis()
if (returnExpr is KtReturnExpression && returnExpr.getTargetLabel() == null) {
val returnValue = returnExpr.analyze()[BindingContext.COMPILE_TIME_VALUE, returnExpr.returnedExpression]
?.getValue(TypeUtils.NO_EXPECTED_TYPE)
if (returnValue == 0) {
// There are no additional overrides in the hierarchy
if (bindingContext[BindingContext.FUNCTION, describeContentsFunction]?.overriddenDescriptors?.size == 1) {
describeContentsFunction.delete()
}
}
}
}
for (property in parcelerObject.declarations.asSequence().filterIsInstance<KtProperty>().filter { it.name == "CREATOR" }) {
if (property.findAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) != null) {
property.delete()
}
}
}
}
@@ -1,34 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiFactory
class ParcelRemoveCustomCreatorProperty(property: KtProperty) : AbstractParcelableQuickFix<KtProperty>(property) {
object Factory : AbstractFactory(f@ {
// KtProperty or its name identifier
psiElement as? KtProperty ?: (psiElement.parent as? KtProperty) ?: return@f null
findElement<KtProperty>()?.let(::ParcelRemoveCustomCreatorProperty)
})
override fun getText() = "Remove custom ''CREATOR'' property"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtProperty) {
element.delete()
}
}
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtPsiFactory
class ParcelRemoveCustomWriteToParcel(function: KtFunction) : AbstractParcelableQuickFix<KtFunction>(function) {
object Factory : AbstractFactory({ findElement<KtFunction>()?.let(::ParcelRemoveCustomWriteToParcel) })
override fun getText() = "Remove custom ''writeToParcel()'' function"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtFunction) {
element.delete()
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import kotlinx.android.parcel.IgnoredOnParcel
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiFactory
class ParcelableAddIgnoreOnParcelAnnotationQuickfix(property: KtProperty) : AbstractParcelableQuickFix<KtProperty>(property) {
object Factory : AbstractFactory({ findElement<KtProperty>()?.let(::ParcelableAddIgnoreOnParcelAnnotationQuickfix) })
override fun getText() = "Add ''@IgnoredOnParcel'' annotation"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtProperty) {
element.addAnnotationEntry(ktPsiFactory.createAnnotationEntry("@" + IgnoredOnParcel::class.java.name)).shortenReferences()
}
}
@@ -1,38 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.createPrimaryConstructorIfAbsent
class ParcelableAddPrimaryConstructorQuickfix(clazz: KtClass) : AbstractParcelableQuickFix<KtClass>(clazz) {
object Factory : AbstractFactory({ findElement<KtClass>()?.let(::ParcelableAddPrimaryConstructorQuickfix) })
override fun getText() = "Add empty primary constructor"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtClass) {
element.createPrimaryConstructorIfAbsent()
for (secondaryConstructor in element.secondaryConstructors) {
if (secondaryConstructor.getDelegationCall().isImplicit) {
val parameterList = secondaryConstructor.valueParameterList ?: return
val colon = secondaryConstructor.addAfter(ktPsiFactory.createColon(), parameterList)
secondaryConstructor.addAfter(ktPsiFactory.createExpression("this()"), colon)
}
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CLASS_FQNAME
import org.jetbrains.kotlin.psi.*
class ParcelableAddSupertypeQuickfix(clazz: KtClassOrObject) : AbstractParcelableQuickFix<KtClassOrObject>(clazz) {
object Factory : AbstractFactory({ findElement<KtClassOrObject>()?.let(::ParcelableAddSupertypeQuickfix) })
override fun getText() = "Add ''Parcelable'' supertype"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtClassOrObject) {
val supertypeName = ANDROID_PARCELABLE_CLASS_FQNAME.asString()
element.addSuperTypeListEntry(ktPsiFactory.createSuperTypeEntry(supertypeName)).shortenReferences()
}
}
@@ -1,43 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid
import org.jetbrains.kotlin.idea.quickfix.QuickFixContributor
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.lexer.KtTokens
class ParcelableQuickFixContributor : QuickFixContributor {
override fun registerQuickFixes(quickFixes: QuickFixes) {
quickFixes.register(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS,
RemoveModifierFix.createRemoveModifierFromListOwnerPsiBasedFactory(KtTokens.INNER_KEYWORD, false))
quickFixes.register(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE, ParcelableAddSupertypeQuickfix.Factory)
quickFixes.register(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR, ParcelableAddPrimaryConstructorQuickfix.Factory)
quickFixes.register(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED, ParcelableAddIgnoreOnParcelAnnotationQuickfix.Factory)
quickFixes.register(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, ParcelMigrateToParcelizeQuickFix.FactoryForWrite)
quickFixes.register(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, ParcelRemoveCustomWriteToParcel.Factory)
quickFixes.register(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED, ParcelMigrateToParcelizeQuickFix.FactoryForCREATOR)
quickFixes.register(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED, ParcelRemoveCustomCreatorProperty.Factory)
quickFixes.register(ErrorsAndroid.REDUNDANT_TYPE_PARCELER, ParcelableRemoveDuplicatingTypeParcelerAnnotationQuickFix.Factory)
quickFixes.register(ErrorsAndroid.CLASS_SHOULD_BE_PARCELIZE, AnnotateWithParcelizeQuickFix.Factory)
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2017 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.parcel.quickfixes
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtPsiFactory
class ParcelableRemoveDuplicatingTypeParcelerAnnotationQuickFix(anno: KtAnnotationEntry) : AbstractParcelableQuickFix<KtAnnotationEntry>(anno) {
object Factory : AbstractFactory({ findElement<KtAnnotationEntry>()?.let(::ParcelableRemoveDuplicatingTypeParcelerAnnotationQuickFix) })
override fun getText() = "Remove redundant ''@TypeParceler'' annotation"
override fun invoke(ktPsiFactory: KtPsiFactory, element: KtAnnotationEntry) {
element.delete()
}
}
@@ -1,144 +0,0 @@
/*
* Copyright 2010-2017 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 com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Key
import org.gradle.api.Project
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ANDROID_COMPILER_PLUGIN_ID
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.DEFAULT_CACHE_IMPL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ENABLED_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.EXPERIMENTAL_OPTION
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
import java.io.Serializable
import java.lang.Exception
var DataNode<ModuleData>.hasAndroidExtensionsPlugin: Boolean
by NotNullableCopyableDataNodeUserDataProperty(Key.create<Boolean>("HAS_ANDROID_EXTENSIONS_PLUGIN"), false)
var DataNode<ModuleData>.isExperimental: Boolean
by NotNullableCopyableDataNodeUserDataProperty(Key.create<Boolean>("ANDROID_EXTENSIONS_IS_EXPERIMENTAL"), false)
private const val DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE = "hashMap"
var DataNode<ModuleData>.defaultCacheImplementation: String
by NotNullableCopyableDataNodeUserDataProperty(Key.create<String>("ANDROID_EXTENSIONS_DEFAULT_CACHE_IMPL"),
DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE)
interface AndroidExtensionsGradleModel : Serializable {
val hasAndroidExtensionsPlugin: Boolean
val isExperimental: Boolean
val defaultCacheImplementation: String
}
class AndroidExtensionsGradleModelImpl(
override val hasAndroidExtensionsPlugin: Boolean,
override val isExperimental: Boolean,
override val defaultCacheImplementation: String
) : AndroidExtensionsGradleModel
@Suppress("unused")
class AndroidExtensionsProjectResolverExtension : AbstractProjectResolverExtension() {
override fun getExtraProjectModelClasses() = setOf(AndroidExtensionsGradleModel::class.java)
override fun getToolingExtensionsClasses() = setOf(AndroidExtensionsModelBuilderService::class.java)
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
val androidExtensionsModel = resolverCtx.getExtraProject(gradleModule, AndroidExtensionsGradleModel::class.java) ?: return
ideModule.hasAndroidExtensionsPlugin = androidExtensionsModel.hasAndroidExtensionsPlugin
ideModule.isExperimental = androidExtensionsModel.isExperimental
ideModule.defaultCacheImplementation = androidExtensionsModel.defaultCacheImplementation
super.populateModuleExtraModels(gradleModule, ideModule)
}
}
class AndroidExtensionsModelBuilderService : ModelBuilderService {
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
return ErrorMessageBuilder.create(project, e, "Gradle import errors")
.withDescription("Unable to build Android Extensions plugin configuration")
}
override fun canBuild(modelName: String?): Boolean = modelName == AndroidExtensionsGradleModel::class.java.name
override fun buildAll(modelName: String?, project: Project): Any {
val androidExtensionsPlugin = project.plugins.findPlugin("kotlin-android-extensions")
val androidExtensionsExtension = project.extensions.findByName("androidExtensions")
val isExperimental = androidExtensionsExtension?.let { ext ->
val isExperimentalMethod = ext::class.java.methods
.firstOrNull { it.name == "isExperimental" && it.parameterCount == 0 }
?: return@let false
isExperimentalMethod.invoke(ext) as? Boolean
} ?: false
val defaultCacheImplementation = androidExtensionsExtension?.let { ext ->
val defaultCacheImplementationMethod = ext::class.java.methods.firstOrNull {
it.name == "getDefaultCacheImplementation" && it.parameterCount == 0
} ?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
val enumValue = defaultCacheImplementationMethod.invoke(ext) ?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
val optionNameMethod = enumValue::class.java.methods.firstOrNull { it.name == "getOptionName" && it.parameterCount == 0 }
?: return@let DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
optionNameMethod.invoke(enumValue) as? String
} ?: DEFAULT_CACHE_IMPLEMENTATION_DEFAULT_VALUE
return AndroidExtensionsGradleModelImpl(androidExtensionsPlugin != null, isExperimental, defaultCacheImplementation)
}
}
@Suppress("unused")
class AndroidExtensionsGradleImportHandler : GradleProjectImportHandler {
override fun importBySourceSet(facet: KotlinFacet, sourceSetNode: DataNode<GradleSourceSetData>) {
val module = ExternalSystemApiUtil.findParent(sourceSetNode, ProjectKeys.MODULE) ?: return
importByModule(facet, module)
}
override fun importByModule(facet: KotlinFacet, moduleNode: DataNode<ModuleData>) {
val facetSettings = facet.configuration.settings
val commonArguments = facetSettings.compilerArguments ?: CommonCompilerArguments.DummyImpl()
fun makePluginOption(key: String, value: String) = "plugin:$ANDROID_COMPILER_PLUGIN_ID:$key=$value"
val newPluginOptions = (commonArguments.pluginOptions ?: emptyArray())
.filterTo(mutableListOf()) { !it.startsWith("plugin:$ANDROID_COMPILER_PLUGIN_ID:") } // Filter out old options
if (moduleNode.hasAndroidExtensionsPlugin) {
newPluginOptions += makePluginOption(EXPERIMENTAL_OPTION.optionName, moduleNode.isExperimental.toString())
newPluginOptions += makePluginOption(ENABLED_OPTION.optionName, moduleNode.hasAndroidExtensionsPlugin.toString())
newPluginOptions += makePluginOption(DEFAULT_CACHE_IMPL_OPTION.optionName, moduleNode.defaultCacheImplementation)
}
commonArguments.pluginOptions = newPluginOptions.toTypedArray()
facetSettings.compilerArguments = commonArguments
}
}
@@ -1,24 +0,0 @@
/*
* 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 com.intellij.diagnostic.ITNReporter
import com.intellij.openapi.diagnostic.IdeaLoggingEvent
class AndroidExtensionsReportSubmitter : ITNReporter() {
override fun showErrorInRelease(event: IdeaLoggingEvent) = true
}
@@ -1,60 +0,0 @@
/*
* 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 com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.ModuleServiceManager
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.xml.XmlAttribute
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class AndroidGotoDeclarationHandler : GotoDeclarationHandler {
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
if (sourceElement is LeafPsiElement && sourceElement.parent is KtSimpleNameExpression) {
val simpleNameExpression = sourceElement.parent as? KtSimpleNameExpression ?: return null
val layoutManager = getLayoutManager(sourceElement) ?: return null
val propertyDescriptor = resolvePropertyDescriptor(simpleNameExpression) ?: return null
val psiElements = layoutManager.propertyToXmlAttributes(propertyDescriptor)
val valueElements = psiElements.mapNotNull { (it as? XmlAttribute)?.valueElement as? PsiElement }
if (valueElements.isNotEmpty()) return valueElements.toTypedArray()
}
return null
}
private fun resolvePropertyDescriptor(simpleNameExpression: KtSimpleNameExpression): PropertyDescriptor? {
val resolvedCall = simpleNameExpression.resolveToCall()
return resolvedCall?.resultingDescriptor as? PropertyDescriptor
}
private fun getLayoutManager(sourceElement: PsiElement): AndroidLayoutXmlFileManager? {
val moduleInfo = sourceElement.getModuleInfo().findAndroidModuleInfo() ?: return null
return ModuleServiceManager.getService(moduleInfo.module, AndroidLayoutXmlFileManager::class.java)
}
override fun getActionText(context: DataContext): String? {
return null
}
}
@@ -1,68 +0,0 @@
/*
* 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.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.incremental.components.LookupLocation
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,
lookupLocation: LookupLocation
) {
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.lazySubpackages) {
if (fragment.isDeprecated) continue
handleScope(fragment.descriptor().getMemberScope())
}
}
}
override fun appendExtensionCallables(
consumer: MutableList<in CallableDescriptor>,
moduleDescriptor: ModuleDescriptor,
receiverTypes: Collection<KotlinType>,
nameFilter: (String) -> Boolean
) {
throw IllegalStateException("Should not be called")
}
}
@@ -1,133 +0,0 @@
/*
* 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 com.intellij.ide.highlighter.XmlFileType
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlToken
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
class AndroidPsiTreeChangePreprocessor : PsiTreeChangePreprocessor, SimpleModificationTracker() {
override fun treeChanged(event: PsiTreeChangeEventImpl) {
if (event.code in HANDLED_EVENTS) {
val child = event.child
// We should get more precise event notification (not just "that file was changed somehow")
if (child == null && event.code == PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED) {
return
}
// Layout file was renamed
val element = event.element
if (element != null
&& event.code == PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED
&& event.propertyName == "fileName") {
if (checkIfLayoutFile(element)) {
incModificationCount()
return
}
}
if (child != null && checkIfLayoutFile(child)) {
incModificationCount()
return
}
val file = event.file ?: return
if (!checkIfLayoutFile(file)) return
val xmlAttribute = findXmlAttribute(child) ?: return
val name = xmlAttribute.name
if (name != "android:id" && name != "class") return
incModificationCount()
}
}
private companion object {
private val HANDLED_EVENTS = setOf(
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED,
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED,
PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED)
private fun checkIfLayoutFile(element: PsiElement): Boolean {
val xmlFile = element as? XmlFile ?: return false
val projectFileIndex = ProjectRootManager.getInstance(xmlFile.project).fileIndex
val module = projectFileIndex.getModuleForFile(xmlFile.virtualFile)
?: element.getContainingDirectorySafe()?.let { projectFileIndex.getModuleForFile(it.virtualFile) }
if (module != null && !module.isDisposed) {
val resourceManager = AndroidLayoutXmlFileManager.getInstance(module) ?: return false
val resDirectories = resourceManager.getAllModuleResDirectories()
val baseDirectory = xmlFile.parent?.parent?.virtualFile
if (baseDirectory != null && baseDirectory in resDirectories && xmlFile.isLayoutXmlFile()) {
return true
}
}
return false
}
private fun PsiFile.getContainingDirectorySafe(): PsiDirectory? {
val virtualFile = this.viewProvider.virtualFile.takeIf { it.isValid } ?: return null
val parentDirectory = virtualFile.parent?.takeIf { it.isValid } ?: return null
return this.manager.findDirectory(parentDirectory)
}
private fun findXmlAttribute(element: PsiElement?): XmlAttribute? {
return when (element) {
is XmlToken, is XmlAttributeValue -> findXmlAttribute(element.parent)
is XmlAttribute -> element
else -> null
}
}
private fun AndroidLayoutXmlFileManager.getAllModuleResDirectories(): List<VirtualFile> {
val module = androidModule ?: return listOf()
val fileManager = VirtualFileManager.getInstance()
return module.variants.fold(arrayListOf<VirtualFile>()) { list, variant ->
for (dir in variant.resDirectories) {
fileManager.findFileByUrl("file://$dir")?.let { list += it }
}
list
}
}
private fun PsiFile.isLayoutXmlFile(): Boolean {
if (fileType != XmlFileType.INSTANCE) return false
return parent?.name?.startsWith("layout") ?: false
}
}
}
@@ -1,75 +0,0 @@
/*
* 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 com.intellij.psi.PsiElement
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.psi.xml.XmlFile
import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
class AndroidSimpleNameReferenceExtension : SimpleNameReferenceExtension {
override fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement): Boolean =
element is XmlFile && reference.isReferenceToXmlFile(element)
private fun isLayoutPackageIdentifier(reference: KtSimpleNameReference): Boolean {
val probablyVariant = reference.element.parent as? KtDotQualifiedExpression ?: return false
val probablyKAS = probablyVariant.receiverExpression as? KtDotQualifiedExpression ?: return false
return probablyKAS.receiverExpression.text == AndroidConst.SYNTHETIC_PACKAGE
}
override fun handleElementRename(reference: KtSimpleNameReference, psiFactory: KtPsiFactory, newElementName: String): PsiElement? {
val resolvedElement = reference.resolve()
if (resolvedElement is XmlAttributeValue && isIdDeclaration(resolvedElement)) {
val newSyntheticPropertyName = androidIdToName(newElementName) ?: return null
return psiFactory.createNameIdentifier(newSyntheticPropertyName.name)
}
else if (isLayoutPackageIdentifier(reference)) {
return psiFactory.createSimpleName(newElementName.removeSuffix(".xml")).getIdentifier()
}
return null
}
private fun isIdDeclaration(declaration: XmlAttributeValue) = declaration.value?.startsWith("@+id/") ?: false
private fun KtSimpleNameReference.isReferenceToXmlFile(xmlFile: XmlFile): Boolean {
if (!isLayoutPackageIdentifier(this)) {
return false
}
if (xmlFile.name.removeSuffix(".xml") != element.getReferencedName()) {
return false
}
val virtualFile = xmlFile.virtualFile ?: return false
val layoutDir = virtualFile.parent
if (layoutDir.name != "layout" && !layoutDir.name.startsWith("layout-")) {
return false
}
val resourceDirectories = AndroidModuleInfoProvider.getInstance(element)?.getAllResourceDirectories() ?: return false
val resourceDirectory = virtualFile.parent?.parent ?: return false
return resourceDirectories.any { it == resourceDirectory }
}
}
@@ -1,58 +0,0 @@
/*
* 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 com.intellij.psi.PsiElement
import com.intellij.psi.XmlElementVisitor
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlElement
import com.intellij.psi.xml.XmlTag
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.android.synthetic.isWidgetTypeIgnored
import org.jetbrains.kotlin.android.synthetic.res.ResourceIdentifier
class AndroidXmlVisitor(val elementCallback: (ResourceIdentifier, String, XmlAttribute) -> Unit) : XmlElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitXmlElement(element: XmlElement?) {
element?.acceptChildren(this)
}
override fun visitXmlTag(tag: XmlTag?) {
val localName = tag?.localName ?: ""
if (isWidgetTypeIgnored(localName)) {
tag?.acceptChildren(this)
return
}
val idAttribute = tag?.getAttribute(AndroidConst.ID_ATTRIBUTE_NO_NAMESPACE, AndroidConst.ANDROID_NAMESPACE)
if (idAttribute != null) {
val idAttributeValue = idAttribute.value
if (idAttributeValue != null) {
val xmlType = tag.getAttribute(AndroidConst.CLASS_ATTRIBUTE_NO_NAMESPACE)?.value ?: localName
val name = androidIdToName(idAttributeValue)
if (name != null) elementCallback(name, xmlType, idAttribute)
}
}
tag?.acceptChildren(this)
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2010-2017 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 com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.model.isAndroidModule
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ANDROID_COMPILER_PLUGIN_ID
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.EXPERIMENTAL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.ENABLED_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidCommandLineProcessor.Companion.DEFAULT_CACHE_IMPL_OPTION
import org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar.Companion.parseCacheImplementationType
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.idea.core.unwrapModuleSourceInfo
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.platform.jvm.isJvm
private val ANNOTATION_OPTION_PREFIX = "plugin:$ANDROID_COMPILER_PLUGIN_ID:"
private fun Module.getOptionValueInFacet(option: AbstractCliOption): String? {
val kotlinFacet = KotlinFacet.get(this) ?: return null
val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return null
val prefix = ANNOTATION_OPTION_PREFIX + option.optionName + "="
val optionValue = commonArgs.pluginOptions
?.firstOrNull { it.startsWith(prefix) }
?.substring(prefix.length)
return optionValue
}
private fun isTestMode(module: Module): Boolean {
return ApplicationManager.getApplication().isUnitTestMode && module.isAndroidModule
}
internal val Module.androidExtensionsIsEnabled: Boolean
get() = isTestMode(this) || getOptionValueInFacet(ENABLED_OPTION) == "true"
internal fun ModuleInfo.findAndroidModuleInfo() = unwrapModuleSourceInfo()?.takeIf { it.platform.isJvm() }
internal val ModuleInfo.androidExtensionsIsEnabled: Boolean
get() {
val module = this.findAndroidModuleInfo()?.module ?: return false
return module.androidExtensionsIsEnabled
}
internal val ModuleInfo.androidExtensionsIsExperimental: Boolean
get() {
val module = this.findAndroidModuleInfo()?.module ?: return false
return module.androidExtensionsIsExperimental
}
internal val Module.androidExtensionsIsExperimental: Boolean
get() {
if (isTestMode(this)) return true
return getOptionValueInFacet(EXPERIMENTAL_OPTION) == "true"
}
val ModuleInfo.androidExtensionsGlobalCacheImpl: CacheImplementation
get() {
val module = this.findAndroidModuleInfo()?.module ?: return CacheImplementation.NO_CACHE
return parseCacheImplementationType(module.getOptionValueInFacet(DEFAULT_CACHE_IMPL_OPTION))
}
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2017 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 kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.synthetic.codegen.AbstractAndroidExtensionsExpressionCodegenExtension
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.psi.KtElement
class IDEAndroidExtensionsExpressionCodegenExtension : AbstractAndroidExtensionsExpressionCodegenExtension() {
override fun isExperimental(element: KtElement?) =
element?.getModuleInfo()?.androidExtensionsIsExperimental ?: false
override fun isEnabled(element: KtElement?) =
element?.getModuleInfo()?.androidExtensionsIsEnabled ?: false
override fun getGlobalCacheImpl(element: KtElement?) =
element?.getModuleInfo()?.androidExtensionsGlobalCacheImpl ?: CacheImplementation.DEFAULT
}
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2017 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.codegen.AbstractAndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.psi.KtElement
class IDEAndroidOnDestroyClassBuilderInterceptorExtension : AbstractAndroidOnDestroyClassBuilderInterceptorExtension() {
override fun getGlobalCacheImpl(element: KtElement) = element.getModuleInfo().androidExtensionsGlobalCacheImpl
}
@@ -1,168 +0,0 @@
/*
* 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.res
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider
import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider.SourceProviderMirror
import org.jetbrains.kotlin.android.synthetic.AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH
import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsExperimental
import org.jetbrains.kotlin.android.synthetic.res.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.io.File
class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) {
override val androidModule: AndroidModule?
get() = AndroidModuleInfoProvider.getInstance(module)?.let { getAndroidModuleInfo(it) }
@Volatile
private var _moduleData: CachedValue<AndroidModuleData>? = null
override fun getModuleData(): AndroidModuleData {
if (androidModule == null) {
_moduleData = null
}
else {
if (_moduleData == null) {
_moduleData = cachedValue(project) {
CachedValueProvider.Result.create(
super.getModuleData(),
getPsiTreeChangePreprocessor(), ProjectRootModificationTracker.getInstance(project))
}
}
}
return _moduleData?.value ?: AndroidModuleData.EMPTY
}
private fun getPsiTreeChangePreprocessor(): PsiTreeChangePreprocessor {
return project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).firstIsInstance<AndroidPsiTreeChangePreprocessor>()
}
override fun doExtractResources(layoutGroup: AndroidLayoutGroupData, module: ModuleDescriptor): AndroidLayoutGroup {
val psiManager = PsiManager.getInstance(project)
val layouts = layoutGroup.layouts.map { psiFile ->
// Sometimes due to a race of later-invoked runnables, the PsiFile can be invalidated; make sure to refresh if possible,
val layout = if (psiFile.isValid) psiFile else psiManager.findFileSafe(psiFile.virtualFile)
val resources = arrayListOf<AndroidResource>()
layout?.accept(AndroidXmlVisitor { id, widgetType, attribute ->
resources += parseAndroidResource(id, widgetType, attribute.valueElement)
})
AndroidLayout(resources)
}
return AndroidLayoutGroup(layoutGroup.name, layouts)
}
private fun PsiManager.findFileSafe(virtualFile: VirtualFile): PsiFile? {
if (virtualFile.isValid)
return findFile(virtualFile)
return null
}
override fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List<PsiElement> {
val fqPath = propertyDescriptor.fqNameUnsafe.pathSegments()
if (fqPath.size <= SYNTHETIC_PACKAGE_PATH_LENGTH) return listOf()
fun handle(variantData: AndroidVariantData, defaultVariant: Boolean = false): List<PsiElement>? {
val layoutNamePosition = SYNTHETIC_PACKAGE_PATH_LENGTH + (if (defaultVariant) 0 else 1)
val layoutName = fqPath[layoutNamePosition].asString()
val layoutFiles = variantData.layouts[layoutName] ?: return null
if (layoutFiles.isEmpty()) return null
val propertyName = propertyDescriptor.name.asString()
val attributes = arrayListOf<PsiElement>()
val visitor = AndroidXmlVisitor { retId, _, valueElement ->
if (retId.name == propertyName) attributes.add(valueElement)
}
layoutFiles.forEach { it.accept(visitor) }
return attributes
}
for (variantData in getModuleData().variants) {
if (variantData.variant.isMainVariant && fqPath.size == SYNTHETIC_PACKAGE_PATH_LENGTH + 2) {
handle(variantData, true)?.let { return it }
}
else if (fqPath[SYNTHETIC_PACKAGE_PATH_LENGTH].asString() == variantData.variant.name) {
handle(variantData)?.let { return it }
}
}
return listOf()
}
private fun SourceProviderMirror.toVariant() = AndroidVariant(name, resDirectories.map { it.canonicalPath })
private fun getAndroidModuleInfo(androidInfoProvider: AndroidModuleInfoProvider): AndroidModule? {
if (androidInfoProvider.module.androidExtensionsIsExperimental) {
return getAndroidModuleInfoExperimental(androidInfoProvider)
}
val applicationPackage = androidInfoProvider.getApplicationPackage()
if (applicationPackage != null) {
val mainVariant = androidInfoProvider.getMainSourceProvider()?.toVariant()
val variantsForFlavorts = androidInfoProvider.getFlavorSourceProviders().map { it.toVariant() }
val allVariants = listOfNotNull(mainVariant) + variantsForFlavorts
if (allVariants.isNotEmpty()) {
return AndroidModule(applicationPackage, allVariants)
}
}
return null
}
private fun getAndroidModuleInfoExperimental(androidFacet: AndroidModuleInfoProvider): AndroidModule? {
val applicationPackage = androidFacet.getApplicationPackage() ?: return null
val appResourceDirectories = androidFacet.getApplicationResourceDirectories(true).mapNotNull { it.canonicalPath }
val resDirectoriesForMainVariant = androidFacet.run {
val resDirsFromSourceProviders = androidFacet.getAllSourceProviders()
.filter { it.name != "main" }
.flatMap { it.resDirectories }
.map { it.canonicalPath }
appResourceDirectories - resDirsFromSourceProviders
}
val variants = mutableListOf(AndroidVariant("main", resDirectoriesForMainVariant))
androidFacet.getActiveSourceProviders()
.filter { it.name != "main" }
.forEach { sourceProvider -> variants += sourceProvider.toVariant() }
return AndroidModule(applicationPackage, variants)
}
}
@@ -1,56 +0,0 @@
/*
* 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.res
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.ClearableLazyValue
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.model.AndroidModuleInfoProvider
import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsEnabled
import org.jetbrains.kotlin.android.synthetic.idea.androidExtensionsIsExperimental
import org.jetbrains.kotlin.android.synthetic.idea.findAndroidModuleInfo
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidPackageFragmentProviderExtension
class IDEAndroidPackageFragmentProviderExtension(val project: Project) : AndroidPackageFragmentProviderExtension() {
override fun isExperimental(moduleInfo: ModuleInfo?): Boolean {
return moduleInfo?.androidExtensionsIsExperimental ?: false
}
override fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? {
val module = moduleInfo?.findAndroidModuleInfo()?.module ?: return null
if (!isAndroidExtensionsEnabled(module)) return null
return ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
}
private fun isAndroidExtensionsEnabled(module: Module): Boolean {
// Android Extensions should be always enabled for Android/JPS
if (isLegacyIdeaAndroidModule(module)) return true
return module.androidExtensionsIsEnabled
}
private fun isLegacyIdeaAndroidModule(module: Module): Boolean {
val infoProvider = AndroidModuleInfoProvider.getInstance(module) ?: return false
return infoProvider.isAndroidModule() && !infoProvider.isGradleModule()
}
override fun <T> createLazyValue(value: () -> T): () -> T {
return { ClearableLazyValue.create<T> { value() }.value }
}
}
@@ -1,7 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp"
android:versionCode="1"
android:versionName="1.0" >
</manifest>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyBu<caret>
}
// EXIST: { lookupString:"MyButton", tailText: " from layout.xml for Activity (Android Extensions)", typeText:"View!" }
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyBu<caret>
}
// EXIST: { lookupString:"MyButton", tailText: " from layout.xml for Fragment (Android Extensions)", typeText:"View!" }
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyBu<caret>
}
// EXIST: { lookupString:"MyButton", tailText: " from layout.xml for Activity (Android Extensions)", typeText:"View!" }
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyBu<caret>
}
// EXIST: { lookupString:"MyButton", tailText: " from layout.xml for Fragment (Android Extensions)", typeText:"View!" }
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,12 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
import kotlinx.android.synthetic.main.layout1.*
class MyActivity: Activity() {
val button = log<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for Activity (Android Extensions)", typeText:"Button!" }
// EXIST: { lookupString:"loginButton", tailText: " from layout1.xml for Activity (Android Extensions)", typeText:"Button!" }
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,12 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
import kotlinx.android.synthetic.main.layout1.*
class MyFragment: Fragment() {
val button = log<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for Fragment (Android Extensions)", typeText:"Button!" }
// EXIST: { lookupString:"loginButton", tailText: " from layout1.xml for Fragment (Android Extensions)", typeText:"Button!" }
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for Activity (Android Extensions)", typeText:"Button!" }
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for Fragment (Android Extensions)", typeText:"Button!" }
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = this.login<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for View (Android Extensions)", typeText:"Button!" }
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
<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>
@@ -1,9 +0,0 @@
package com.myapp
import android.app.Activity
class MyActivity: Activity() {
val button = this.login<caret>
}
// EXIST: { lookupString:"login", tailText: " from layout.xml for Activity (Android Extensions)", typeText:"Button!" }
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,17 +0,0 @@
// SUGGESTED_NAMES: view, getButton
// SUGGESTED_RETURN_TYPES: android.view.View?, android.view.View
// PARAM_DESCRIPTOR: public final class MyActivity : android.app.Activity defined in test in file toTopLevelFun.kt
// PARAM_TYPES: test.MyActivity, android.app.Activity
package test
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
// SIBLING:
class MyActivity: Activity() {
fun test() {
val button = <selection>MyButton</selection>
}
}
@@ -1,20 +0,0 @@
// SUGGESTED_NAMES: view, getButton
// SUGGESTED_RETURN_TYPES: android.view.View?, android.view.View
// PARAM_DESCRIPTOR: public final class MyActivity : android.app.Activity defined in test in file toTopLevelFun.kt
// PARAM_TYPES: test.MyActivity, android.app.Activity
package test
import android.app.Activity
import android.view.View
import kotlinx.android.synthetic.main.layout.*
// SIBLING:
class MyActivity: Activity() {
fun test() {
val button = view()
}
}
private fun MyActivity.view(): View? = MyButton
@@ -1,9 +0,0 @@
package test
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
}
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package test
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
}
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package test
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
}
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package test
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
}
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package test
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
val button1 = this.loginButton
}
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package test
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
val button1 = this.loginButton
}
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,14 +0,0 @@
package test
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = login<caret>
fun f() = login.toString()
}
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,13 +0,0 @@
package test
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyFragemnt : Fragment() {
override fun onResume() {}
val button = login<caret>
fun f() = login.toString()
}
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,8 +0,0 @@
package test
import android.view.View
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = login<caret>
}
@@ -1,20 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,14 +0,0 @@
package test
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = login<caret>
fun f() = login.toString()
}
@@ -1,12 +0,0 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = login<caret>
}
@@ -1,17 +0,0 @@
<FrameLayout xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
a:id="@+id/item_detail_container"
a:layout_width="match_parent"
a:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
a:id="@+id/login"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
}
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
}
@@ -1,15 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
}
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,9 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
}
@@ -1,14 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
val button1 = this.loginButton
}
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +0,0 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
val button1 = this.loginButton
}
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,29 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,12 +0,0 @@
package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
val button = login<caret>
}
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,11 +0,0 @@
package com.myapp
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.main.layout.*
public class MyFragment : Fragment() {
override fun onResume() {}
val button = login<caret>
}
@@ -1,17 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>

Some files were not shown because too many files have changed in this diff Show More